Effective Visual
Representation
Visual representation has become a cornerstone of effective
communication. From simple bar charts to complex interactive dashboards,
visualizations serve as powerful tools for summarizing, analyzing, and
conveying information. The success of any visual representation hinges
on its graphical components, which are the fundamental elements that
bring data to life. These components, when used effectively, ensure
clarity, precision, and engagement.
Required Graphical
Components
col0 = c("#4682B4", "#B4464B", "#B4AF46")
col.vec = ifelse(penguins$species == "Adelie", "#4682B4",
ifelse(penguins$species == "Chinstrap", "#B4464B", "#B4AF46"))
par(mfrow=c(1,2), oma=c(0,0,2,0))
layout <- layout(matrix(c(1,1,2,3), 2, 2, byrow = F))
layout <- plot(penguins$bill_length_mm, penguins$bill_depth_mm,
col= col.vec,
pch=16,
xlab="Bill Depth (mm)",
ylab="Bill Length (mm)",
main="A: Bill Length by Bill Depth ",
cex.main = 0.9,
col.main = "blue")
legend("bottomright", c("Adelie", "Chinstrap", "Gentoo"), pch=rep(16,3),
col = col0,
cex = 0.7, bty = "n")
layout <- boxplot(penguins$bill_depth_mm ~ penguins$species,
xlab="Species",
ylab="Bill Depth (mm)",
col = col0,
main="B: Bill Depth by species",
cex.main = 0.9,
col.main = "blue")
layout <- boxplot(penguins$bill_length_mm ~ penguins$species,
xlab="Species",
ylab="Bill Length (mm)",
col = col0,
main="C: Bill Length by species",
cex.main = 0.9,
col.main = "blue")
###
layout <- mtext("Distribution and Coorlation Plots", side = 3, line = -0.5, outer = TRUE)
We next briefly explain the key graphical components in the above
illustrative figure.
Data Points: The Heart of the Visualization
At the core of any visual representation are the data points. These
individual markers encapsulate the values or observations within the
dataset, forming the basis for patterns, trends, or relationships. For
instance, in a scatter plot, each point represents a unique pairing of
variables, while in a line chart, connected points reveal the trajectory
of change over time. Without data points, a visualization would lack
substance, making them the indispensable foundation of any graphical
display.
Axes: The Framework of Interpretation
Axes provide the structural framework that anchors data points in a
meaningful context. Typically represented as horizontal and vertical
lines, axes define the coordinate space and allow viewers to interpret
the scale and scope of the data. The X-axis often represents categories
or time intervals, while the Y-axis denotes numerical values or metrics.
Proper scaling, labeling, and unit representation on axes are critical
for ensuring that data is interpreted accurately. For example, in a bar
chart depicting revenue growth, a properly scaled Y-axis starting at
zero prevents misrepresentation of trends.
Labels: Guiding the Viewer
Labels are essential textual elements that provide clarity and
context to visual components. Axis labels describe the variables being
measured, while data labels pinpoint specific values, enhancing the
precision of the representation. For instance, a pie chart with clearly
labeled slices ensures that viewers can quickly grasp the proportions of
different categories. Thoughtfully designed labels not only aid
interpretation but also enhance the overall usability of the
visualization.
Legends: Decoding the Representation
Legends act as the key to understanding the symbols, colors, or
patterns used in a visualization. Particularly in multi-variable
representations, legends enable viewers to differentiate between
categories or datasets. For example, a line chart with multiple colored
lines representing different regions relies on a legend to clarify the
mapping between colors and regions. A well-designed legend is concise,
strategically placed, and aligned with the overall design of the
visualization.
Color: Enhancing Communication
Color is one of the most powerful tools in a visual representation,
capable of conveying information, drawing attention, and evoking
emotions. Strategic use of color enhances the interpretability of data
by differentiating categories, highlighting trends, or emphasizing
critical points. For example, a heatmap uses color gradients to
represent intensity or magnitude, while contrasting colors in a bar
chart distinguish between categories. Accessibility considerations, such
as using colorblind-friendly palettes, are essential to ensure
inclusivity and broad usability.
Titles, Captions, and Annotations: Providing
Context
Titles, captions, and annotations are textual components that offer
context, summarize insights, or explain specific aspects of the
visualization. The title acts as the headline, succinctly stating the
purpose or takeaway of the visual. Captions provide supplementary
information, such as data sources or methodology, while annotations
highlight key trends, outliers, or benchmarks. Together, these
components guide viewers through the data story and reinforce the
intended message.
In summary, each graphical component contributes uniquely to the
clarity and functionality of a visualization. When thoughtfully
integrated, these elements work together to create a design that is
intuitive, visually appealing, and effective in communicating its
message.
An intuitive visualization ensures the audience can interpret
information easily and accurately, without confusion or ambiguity.
Aesthetics, such as harmonious color schemes and clean layouts, further
enhance engagement and comprehension. By balancing clarity,
functionality, and design, a well-crafted visualization transforms data
into meaningful insights, empowering storytelling.
Purposes of Viz
Representation
Visual representation plays a pivotal role in the field of data
science, especially during Exploratory Data Analysis (EDA) and feature
engineering. These two phases are critical for understanding datasets,
identifying patterns, and preparing data for machine learning models.
The use of visualizations not only simplifies complex data but also
enables analysts to uncover insights that may be obscured in raw
numbers. In this context, understanding the purposes and types of visual
representation becomes essential for effective analysis and feature
development.
The purposes of visual representation can be broadly categorized into
the following key areas.
Understanding Data Distribution
Visualizations help data scientists examine the distribution of
individual variables. Charts such as histograms, density plots, and box
plots are commonly used to identify the central tendency, spread,
skewness, and presence of outliers. For instance, a histogram may reveal
that a variable is right-skewed, suggesting the need for transformation
to normalize the data.
Identifying Patterns and Relationships
During EDA, visualizations reveal patterns and relationships between
variables. Scatter plots, pair plots, and heatmaps allow analysts to
explore correlations and dependencies. For example, a scatter plot might
show a strong positive correlation between advertising spend and sales,
guiding feature selection for predictive models.
Detecting Anomalies and Outliers
Outliers can significantly impact machine learning models, making
their detection crucial. Visual tools like box plots and scatter plots
enable analysts to pinpoint anomalies effectively. For instance, a box
plot of transaction amounts in a dataset may highlight unusually high
values indicative of potential fraud.
Validating Data Quality
Visualizations help assess data quality by highlighting missing
values, duplicates, or inconsistent patterns. Heatmaps or bar plots can
be used to visualize missing data distributions, guiding the choice of
imputation strategies.
Guiding Feature Engineering
Feature engineering involves creating or transforming features to
improve model performance. Visualizations such as correlation matrices
and decision boundary plots can inspire new feature combinations or
transformations. For example, a high correlation between two variables
might suggest creating an interaction term, while patterns in a scatter
plot could indicate the need for feature scaling.
Communicating Insights
Visualizations are an effective way to communicate findings from EDA
and feature engineering to stakeholders. Clear, concise graphs help
bridge the gap between technical analysis and business decisions. For
instance, a bar chart illustrating the importance of specific features
can guide discussions on resource allocation or strategic focus.
Types of Visual
Representation
Visual representation is a cornerstone of Exploratory Data Analysis
(EDA) and feature engineering, providing data scientists with tools to
understand data, identify patterns, and prepare features for machine
learning models. Different types of visualizations serve specific
purposes, from understanding variable distributions to detecting
outliers and relationships. Below are the primary types of visual
representation used in these stages of data science.
Univariate Visualizations
Univariate visualizations focus on the distribution and
characteristics of a single variable, helping to assess its central
tendency, variability, and potential outliers. Depending on the types
feature variables, the following charts are commonly used in
practice
- Histograms: Used to display the frequency
distribution of numerical variables, revealing skewness, modality, or
gaps in the data.
- Box Plots: Highlight the spread, median, quartiles,
and potential outliers, providing a concise summary of the variable’s
distribution.
- Density Plots: Smoothened versions of histograms
that show the probability density function, making them useful for
comparing distributions.
- Bar Charts: Ideal for visualizing the frequency of
categorical variables, giving an overview of the dataset
composition.
# Simulating sample data
data <- data.frame(Category = rep(c("A", "B"), each = 50),
Value = c(rnorm(50, mean = 5), rnorm(50, mean = 7)))
boxplot(data$Value ~ data$Category,
xlab="Category",
ylab="Value",
col = c("skyblue", "purple"),
main="Comparison between two Distributions",
cex.main = 1.1,
col.main = "navy")
Bivariate Visualizations
Bivariate visualizations explore relationships between two variables,
uncovering dependencies or correlations that may guide feature selection
or transformation.
- Scatter Plots: Used to identify correlations,
clusters, or trends between two numerical variables.
- Line Charts: Suitable for temporal data, showing
trends or changes over time.
- Grouped Bar Charts: Useful for comparing categories
across multiple groups or subgroups.
- Heatmaps: Display correlations between variables
using color gradients, making them a popular choice for visualizing
correlation matrices.
# Load iris dataset
data(iris)
mrg <- list(l = 50, r = 50, b = 50, t = 50, pad = 20)
##
pal <- c("#1b9e77", "#d95f02", "#7570b3")
pal <- setNames(pal, c("virginica", "setosa", "versicolor"))
#
fig <- plot_ly(data = iris, x = ~Sepal.Length, y = ~Petal.Length,
color = ~Species, colors = pal,
marker = list(size = ~Sepal.Length*2)) %>%
layout(title = 'Correlation between Petal Length and Sepal Length',
plot_bgcolor = "white",
xaxis = list(title = 'Sepal Length (cm)'),
yaxis = list(title = 'Sepal Width (cm)'),
legend = list(x = 0.7,
y = 0.1,
title=list(text='<b> Species of Iris </b>')),
margin = mrg)
fig
Multivariate Visualizations
Multivariate visualizations provide insights into relationships among
three or more variables, helping to identify complex interactions.
- Pair Plots: Offer a grid of scatter plots for all
pairs of variables, along with histograms for individual variables,
enabling a quick overview of relationships.
- Interactive 3D Scatter Plots: Visualize
relationships in three dimensions, adding depth to the analysis of
feature interactions. caution - static 3D scatter
should be avoided.
- Parallel Coordinates: Represent multiple variables
for each data point as connected line segments, useful for visualizing
trends in high-dimensional data.
- Bubble Charts: Add a third dimension to scatter
plots through bubble size, often used to incorporate a weight or
magnitude factor.
names(iris) = c("Sepal.L", "Sepal.W", "Petal.L", "Petal.W", "Species" )
p <- ggpairs(iris, columns=1:4,
aes(color = factor(Species), alpha = 0.5),
upper = list(continuous = wrap("cor", size = 3)),
margin = list(l = 50, r = 50, b = 50, t = 50, pad = 20)) +
ggtitle("Pairwised Scatter Plot of Iris Data") +
theme(
plot.title = element_text(hjust = 1), # Center-align the title
plot.margin = unit(c(1, 2, 2, 1), "cm") # adjust the margin of the plot
)
#
ggplotly(p)
Visualizations for Categorical Data
Categorical data often requires distinct types of visual
representation to highlight distributions and comparisons.
- Stacked Bar Charts: Display the composition of
categories within subgroups, aiding in the analysis of proportions.
- Mosaic Plots: Provide a detailed view of the
relationships between two or more categorical variables through
proportional areas.
- Violin Plots: Combine box plots and density plots
to show the distribution of numerical data across categories.
# Example dataset
data <- data.frame(
Category = c("A", "A", "B", "B", "C", "C"),
Subcategory = c("X", "Y", "X", "Y", "X", "Y"),
Value = c(10, 20, 15, 25, 30, 10)
)
#
# Create the stacked bar chart
plot <- plot_ly(
data,
x = ~Category,
y = ~Value,
color = ~Subcategory,
type = "bar"
) %>%
layout(
barmode = "stack", # Stack the bars
title = "Stacked Bar Chart",
xaxis = list(title = "Category"),
yaxis = list(title = "Value")
)
# Display the plot
plot
Specialized Visualizations for EDA
Certain visualizations cater specifically to the needs of EDA,
focusing on data quality and structure.
- Missing Data Heatmaps: Highlight the distribution
and frequency of missing values across the data set, guiding imputation
strategies.
- Histogram Facets: Display distributions of a
numerical variable across levels of a categorical variable.
- Outlier Plots: Highlight anomalies using scatter
plots or specialized visualizations like box plot whiskers.
# Example dataset
data <- data.frame(
Value = c(rnorm(100, mean = 5), rnorm(100, mean = 10)),
Category = rep(c("Group 1", "Group 2"), each = 100)
)
# Create the faceted histogram
gg = ggplot(data, aes(x = Value, fill = Category)) +
geom_histogram(aes(color = Category), bins = 20, alpha = 0.7, size = 1) +
facet_wrap(~ Category, ncol = 1) + # Facet by Category
scale_color_manual(values = c("Group 1" = "blue", "Group 2" = "green")) +
scale_fill_manual(values = c("Group 1" = "lightblue", "Group 2" = "lightgreen")) +
labs(
title = "Histogram Facets with Colored Boundaries",
x = "Value",
y = "Frequency"
) +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5),
strip.text = element_text(size = 12, face = "bold"),
legend.position = "none" # Remove legend if it's redundant
)
ggplotly(gg)
Visualizations for Feature Engineering
Feature engineering relies on visualizations to assess the
effectiveness of transformations, combinations, and feature
importance.
- Correlation Matrices: Display pairwise correlations
between numerical variables, helping identify redundant or highly
correlated features.
- Feature Importance Plots: Derived from machine
learning models, these plots rank features based on their impact on
model performance.
- Decision Boundary Visualizations: Show the regions
created by a model’s predictions, useful for evaluating feature
combinations.
- Interaction Plots: Illustrate the combined effect
of two or more features on a target variable, often used in regression
or classification problems.
# Example dataset
data <- data.frame(
Factor1 = rep(c("Low", "High"), each = 6),
Factor2 = rep(c("A", "B", "C"), times = 4),
Response = c(5, 6, 7, 10, 11, 12, 4, 5, 6, 9, 8, 10)
)
# Create the interaction plot
interact = ggplot(data, aes(x = Factor2, y = Response, group = Factor1, color = Factor1)) +
geom_line(size = 1) + # Lines representing interaction
geom_point(size = 2) + # Points for data
labs(
title = "Interaction Plot",
x = "Factor 2",
y = "Response",
color = "Factor 1"
) +
theme_minimal() +
theme(
plot.title = element_text(size = 14, face = "bold", hjust = 0.5),
axis.text = element_text(size = 12),
legend.position = "top"
)
ggplotly(interact)
Interactive Visualizations
Interactive visualizations enhance exploration by allowing users to
manipulate the data dynamically.
- Dashboards: Combine multiple visualizations into a
single interface, enabling filtering, zooming, and slicing of data for
deeper insights.
- Geospatial Maps: Visualize geographic data with
layers of interactivity, such as zooming into specific regions or
displaying detailed tooltips.
- Interactive Scatter Plots: Allow users to hover
over points for detailed information or filter data based on variable
ranges.
In summary, the diversity of visual representation in EDA and feature
engineering underscores its importance in understanding and preparing
data for analysis. From univariate histograms to interactive dashboards,
each type serves a unique purpose in exploring patterns, assessing data
quality, and optimizing features for machine learning models. By
selecting the appropriate visualization for the task at hand, data
scientists can uncover insights, enhance communication, and drive better
decision-making.
Model-based
Visualization
Visualization is vital in data science and machine learning (ML)
because it transforms data, results, and model insights into
comprehensible visual forms, aiding exploration, communication, and
decision-making.
Model Performance
Evaluation
Visualization is critical for evaluating and comparing model
performance.
Example: ROC Curve for Classification
library(pROC)
# Simulated classification results
ID = sample(1:150, 150, replace = FALSE)
true_labels <- c(rep(1, 50), rep(0,100))[ID]
predicted_probs <- c(rnorm(50, 3, 7), rnorm(100, 10,4))[ID]
# Plot ROC curve
roc_obj <- roc(true_labels, predicted_probs)
sen = roc_obj$sensitivities
spe = roc_obj$specificities
##
par(pty = "s")
plot(1-spe, sen, type = "l", lwd = 2, lty = 1, col = "blue",
xlab = "1 - specificity",
ylab = "sensitivity",
main = "ROC Curve")
abline(0,1, lty = 2, lwd = 2, col = "red")
legend("bottomright", c("model-based Performance", "Random Guess"),
col=c("blue", "red"), lty=1:2, lwd = 2:1, bty = "n", cex = 0.8)

An interactive plot provides more granular information about the
underlying plot.
library(pROC)
# Simulated classification results
ID = sample(1:150, 150, replace = FALSE)
true_labels <- c(rep(1, 50), rep(0,100))[ID]
predicted_probs <- c(rnorm(50, 3, 7), rnorm(100, 10,4))[ID]
# Plot ROC curve
roc_obj <- roc(true_labels, predicted_probs)
sen = roc_obj$sensitivities
spe = roc_obj$specificities
SenSpe = data.frame(sen = sen, spe = spe)
##
ggroc = ggplot(data = SenSpe, mapping = aes(x =(1-spe), y = sen)) +
geom_rect(aes(xmin = 0,
xmax = 1,
ymin = 0,
ymax = 1),
fill = "transparent",
color = "darkgray",
size = 0.4 # default is 0.5
) +
coord_cartesian(xlim = c(0,1), ylim = c(0,1)) +
#geom_smooth(method = "loess", span = 0.1, col = "darkblue") +
#geom_line(col = "darkblue") +
geom_path(col = "darkblue") +
#geom_abline(col="darkred", linetype="dashed") +
geom_segment(aes(x = 0, y = 0, xend = 1, yend = 1),
color = "darkred", linetype = "dash") +
ggtitle("Interactive ROC Curve") +
theme(plot.title = element_text(
family = "serif", # Font family
face = "bold", # Font face
color = "navy", # Font color
size = 14, # Font size
hjust = 0.5, # Horizontal adjustment
vjust = 1, # Vertical adjustment
angle = 0 # Font angle
),
aspect.ratio = 1)
ggplotly(ggroc)
Understanding Model
Predictions
Visualization explains how models arrive at their predictions,
essential for trust and transparency.
Example: Feature Importance in Random Forest
library(randomForest)
# Fit a random forest model
rf_model <- randomForest(Species ~ ., data = iris, importance = TRUE)
# Plot feature importance
varImpPlot(rf_model, main = "Feature Importance in Random Forest")

rf_model$importance
setosa versicolor virginica MeanDecreaseAccuracy
Sepal.L 0.044408946 0.021189755 0.049340291 0.037682972
Sepal.W 0.009232606 0.004053565 0.008073661 0.007021898
Petal.L 0.356910263 0.310607891 0.289790133 0.315754991
Petal.W 0.314521971 0.296444967 0.261702035 0.288212492
MeanDecreaseGini
Sepal.L 10.991629
Sepal.W 2.258437
Petal.L 42.087532
Petal.W 43.879176
R Interactive Graphical
Functions
Interactive visualizations have become an indispensable tool in
modern data science. Whether you’re preparing a report, a presentation,
or a dashboard, providing interactive figures can make the data more
engaging, insightful, and accessible. In R, several powerful packages
allow you to create dynamic, interactive plots. In the previous section,
I used ggpairs, plot_ly,
ggplotly, and leaflet to illustrate various R
plots. In this section, we will compare some popular packages for
interactive data visualizations in R, including ggiraph,
plotly, ggplotly, and
highcharter. These libraries are often used in conjunction
with the umbrella-package tidyverse for
data manipulation.
We will use these libraries to visualize relationship between two
numerical variables and the distribution of numerical variable. The
examples are based on the popular
Palmer Archipelago (Antarctica) penguin dataset which is
similar to the well-known iris. It is a great introductory
data set for data exploration and visualization.
include_graphics("img/penguinImage.jpg")
A copy of the data with missing values deleted is available at https://pengdsci.github.io/STA552/w02/penguin.csv.
penguins = read.csv("https://pengdsci.github.io/STA552/w02/penguin.csv")
The variables are described below:
- species a factor denoting penguin species (Adélie,
Chinstrap and Gentoo)
- island a factor denoting island in Palmer
Archipelago, Antarctica (Biscoe, Dream or Torgersen)
- bill_length_mm a number denoting bill length
(millimeters)
- bill_depth_mm a number denoting bill depth
(millimeters)
- flipper_length_mm an integer denoting flipper
length (millimeters)
- body_mass_g an integer denoting body mass
(grams)
- sex a factor denoting penguin sex (female,
male)
- year an integer denoting the study year (2007,
2008, or 2009)
Visualizing
Relationship
Two of the 8 feature variables bill_length_mm and
bill_depth_mm will be used to demonstrate the
interactive plots to visualize the relationship between them with
different R graphical functions.
ggiraph
The interactive graphical function ggiraph() is a
wrapper of ggplot(). We first make a ggplot and then
call ggiraph() to add interactive feature to the
ggplot.
fig_full= penguins %>%
ggplot(aes(x = bill_length_mm,
y = bill_depth_mm,
color = species,
shape = species)) +
geom_point_interactive(
aes(tooltip = paste("bill_length_mm:", bill_length_mm,
"<br>", "bill_depth_mm:", bill_depth_mm)),
size = 2) +
geom_smooth_interactive(method = "lm", formula = "y ~ x") +
labs(x = "Bill length (mm)",
y = "Bill width (mm)",
title = "Bill length vs. bill width",
subtitle = "Using the ggiraph package",
color = "Species", shape = "Species") +
theme_minimal() +
theme(plot.title = element_text(family = "serif", # Font family
face = "bold", # Font face
color = "navy", # Font color
size = 14, # Font size
hjust = 0.5, # Horizontal adjustment
vjust = 1, # Vertical adjustment
angle = 0, # Font angle
lineheight = 1), # Line spacing
plot.subtitle = element_text( # Subtitle customization
face = "bold", # Font face
color = "navy", # Font color
size = 12, # Font size
hjust = 0.5, # Horizontal adjustment
vjust = 1, # Vertical adjustment
angle = 0, # Font angle
lineheight = 1), # Line spacing
plot.caption = element_text(hjust = 0.25), # Caption customization
plot.tag = element_text(face = "italic"), # Tag customization
plot.title.position = "plot", # Title and subtitle position
# ("plot" or "panel")
plot.caption.position = "panel", # Caption position ("plot" or "panel")
plot.tag.position = "top", # Tag position
plot.margin = unit(c(0,1,4,1), "cm")) # Margins (t, r, b, l)
girafe(ggobj = fig_full)
plot_ly()
In R, plot_ly() is a function from the Plotly
package used to create interactive plots directly by
specifying the data and plot type. It is a core function in the
plotly package and serves as the starting point for
building various types of visualizations such as scatter plots, line
charts, bar charts, and more. The key features are
Dynamic Interactivity: it supports zooming,
panning, and tooltips (hover effects) for exploring data
interactively.
Customizable Visuals: It allows customization of
colors, markers, axis labels, legends, and more.
Wide Range of Plot Types: It handles scatter
plots, bar charts, histograms, 3D plots, heatmaps, and more.
Layered Plotting: It enables adding multiple
traces (data layers) for creating complex visualizations.
Integration: It works seamlessly with R
Markdown, Shiny, and dashboards.
plotly.fig = penguins %>%
plot_ly(x = ~bill_length_mm,
y = ~flipper_length_mm,
color = ~species,
symbol = ~species,
type = "scatter",
mode = "markers",
marker = list(size = 10)) %>%
layout(
plot_bgcolor = 'white',
xaxis = list(title = "Bill Length (mm)",
zeroline = FALSE,
ticklen = 5),
yaxis = list(title = "Flipper Length (mm)",
zeroline = FALSE,
ticklen = 5),
title = "Bill length vs. bill width",
legend = list(x = 0.01,
y = 0.99,
title=list(text='<b> Species of Penguins </b>')),
margin = list(l = 50, r = 50, b = 50, t = 50, pad = 20)
)
plotly.fig
ggplotly
ggplotly() is a powerful tool for adding interactivity
to existing ggplot2 plots in R. It bridges the gap
between static and dynamic visualizations, allowing users to retain the
familiar ggplot2 workflow while benefiting from
Plotly’s interactivity. This makes it an ideal choice for
exploratory data analysis, presentations, and dynamic reporting.
The key features of ggplotly() are
Interactivity: It adds zoom, pan, hover
tooltips, and legend interaction to static ggplot2
plots.
Preserves ggplot2 Aesthetics: It maintains the
visual design and customization of ggplot2 plots while
making them interactive.
Compatibility: It works seamlessly with
ggplot2-based plots and integrates well with R Markdown, Shiny apps, and
dashboards.
Customization: It allows further enhancement of
interactivity by modifying tooltips, legends, and layouts.
fig_full <- penguins %>%
ggplot(aes(x = bill_length_mm, y = bill_depth_mm,
color = species, shape = species)) +
geom_point(size = 2) +
geom_smooth(method = "lm", formula = "y ~ x") +
labs(x = "Bill length (mm)",
y = "Bill width (mm)",
title = "Bill length vs. bill width",
subtitle = "Using ggplot2 and ggplotly() from plotly",
color = "Species",
shape = "Species") +
theme_minimal() +
theme(plot.title = element_text(family = "serif", # Font family
face = "bold", # Font face
color = "navy", # Font color
size = 14, # Font size
hjust = 0.5, # Horizontal adjustment
vjust = 1, # Vertical adjustment
angle = 0, # Font angle
lineheight = 1), # Line spacing
plot.subtitle = element_text( # Subtitle customization
face = "bold", # Font face
color = "navy", # Font color
size = 12, # Font size
hjust = 0.5, # Horizontal adjustment
vjust = 1, # Vertical adjustment
angle = 0), # Font angle
plot.caption = element_text(hjust = 0.25), # Caption customization
plot.tag = element_text(face = "italic"), # Tag customization
plot.title.position = "plot", # Title and subtitle position
# ("plot" or "panel")
plot.caption.position = "panel", # Caption position ("plot" or "panel")
plot.tag.position = "top", # Tag position
plot.margin = unit(c(1,1,1,1), "cm")) # Margins (t, r, b, l)
ggplotly(fig_full)
highcharter()
In R, highcharter is a package that provides an interface to the
Highcharts JavaScript library, allowing users to create highly
interactive and customizable visualizations. It is particularly
well-suited for creating charts with advanced interactivity, polished
aesthetics, and a wide range of chart types, making it popular for
dashboards, presentations, and web-based reporting.
The key features of highcharter():
Wide Range of Chart Types: It includes bar
charts, line charts, scatter plots, heatmaps, treemaps, and more. It
also supports advanced visualizations like gauge charts, stock charts,
and maps.
Interactive Features: It allows hover tooltips,
zooming, panning, and legends. It also allows dynamic updates and
animations.
Customizability: It offers detailed control over
chart aesthetics, including colors, themes, axis labels, and
tooltips.
Integration: It works seamlessly with tidyverse
for data manipulation. It is compatible with R Markdown and Shiny for
creating dynamic reports and dashboards.
Support for Themes: It allows predefined themes
such as hc_theme_smpl() and the ability to create custom
themes.
#penguins$colors = ifelse(penguins$species =="Adelie","#440154",
# ifelse(penguins$species =="Chinstrap", "#21908C", "#FDE725" ))
##
fig_highchart <- penguins %>%
hchart(type = "scatter",
hcaes(x = bill_length_mm,
y = flipper_length_mm,
group = species),
marker = list(radius = 5),
showInLegend = F) %>%
hc_xAxis(title = list(text = "Bill length (mm)")) %>%
hc_yAxis(title = list(text = "Bill width (mm)")) %>%
hc_title(text = "Bill length vs. bill width") %>%
#hc_colors(colors) %>%
hc_legend(align = "left",
verticalAlign = "top",
layout = "vertical",
x = 0,
y = 100)
##
species_unique <- sort(unique(penguins$species))
colors <- c("Adelie" = "#2caffe", "Chinstrap" = "#544fc5", "Gentoo" = "#00e272")
###
fig_highchart0 = fig_highchart
for(j in 1:3) {
penguins_subset <- penguins %>%
filter(species == species_unique[j])
##
regression <- augment(lm(flipper_length_mm ~ bill_length_mm,
data = penguins_subset))
##
fig_highchart01 <- fig_highchart0 %>%
hc_add_series(regression, "line", hcaes(x = bill_length_mm, y = .fitted),
color = colors[j])
##
fig_highchart0 = fig_highchart01 # updating model object to add another line
}
##
fig_highchart01
Visualizing
Distributions
In this subsection, we draw density curves of BMI across the species
of penguins with the above four graphical functions.
ggiraph
fig_density <- penguins %>%
ggplot(aes(x = body_mass_g, color = species, fill = species)) +
geom_density_interactive(
aes(tooltip = paste("Species:", species)),
linewidth = 0.75,
alpha = 0.5 ) +
xlab("BMI Index (g)") +
ggtitle("Density Curve of BMI across Species") +
theme(plot.margin = unit(c(0,1,4,1), "cm"),
plot.title = element_text(hjust = 0.5))
girafe(ggobj = fig_density)
plot_ly()
Adelie.BMI <- penguins$body_mass_g[penguins$species=="Adelie"]
Chinstrap.BMI <- penguins$body_mass_g[penguins$species=="Chinstrap"]
Gentoo.BMI <- penguins$body_mass_g[penguins$species=="Gentoo"]
Adelie.fit= density(Adelie.BMI)
Chinstrap.fit= density(Chinstrap.BMI)
Gentoo.fit= density(Gentoo.BMI)
# Create overlay density curves
plot_ly() %>%
add_trace(x = Adelie.fit$x,
y = Adelie.fit$y,
mode = "lines",
fill = "tozeroy",
yaxis = "y2",
name = "Adelie",
line = list(color = "blue"),
opacity = 0.6) %>%
add_trace(x = Chinstrap.fit$x,
y = Chinstrap.fit$y,
mode = "lines",
fill = "tozeroy",
yaxis = "y2",
name = "Chinstrap",
line = list(color = "red"),
opacity = 0.6) %>%
add_trace(x = Gentoo.fit$x,
y = Gentoo.fit$y,
mode = "lines",
fill = "tozeroy",
yaxis = "y2",
name = "Gentoo",
line = list(color = "purple"),
opacity = 0.6) %>%
layout(
title = "Distributions of BMI across Penguin Species ",
xaxis = list(title = "BMI Index"),
yaxis = list(title = "Density"),
legend = list(title = list(text = "Species", orientation='h')),
margin = list(l = 100, r = 50, b = 70, t = 100, pad = 20)
)
ggplotly()
ggplotly_density <- penguins %>%
ggplot(aes(x = body_mass_g, color = species, fill = species)) +
geom_density(linewidth = 0.75, alpha = 0.5) +
xlab("BMI Index (g)") +
ggtitle("Density Curve of BMI across Species") +
theme(plot.margin = unit(c(2,1,2,1), "cm"),
plot.title = element_text(hjust = 0.5))
ggplotly(ggplotly_density)
highcharter()
Adelie <- penguins %>% filter(species == "Adelie")
Chinstrap <- penguins %>% filter(species == "Chinstrap")
Gentoo <- penguins %>% filter(species == "Gentoo")
hc <- hchart(
density(Adelie$body_mass_g),
type = "area",
color = "steelblue",
name = "Adelie") %>%
hc_add_series(
density(Chinstrap$body_mass_g),
type = "area",
color = "#B71C1C",
name = "Chinstrap") %>%
hc_add_series(
density(Gentoo$body_mass_g),
type = "area",
color = "purple",
name = "Gentoo") %>%
hc_title(text = "Density Curves of BMI across Species") %>%
hc_legend(align = "left",
verticalAlign = "top",
layout = "vertical",
x = 0,
y = 100)
hc
LS0tDQp0aXRsZTogJ1Zpc3VhbCBSZXByZXNlbnRhdGlvbiBmb3IgTWFjaGluZSBMZWFybmluZycNCmF1dGhvcjogIkNoZW5nIFBlbmciDQpkYXRlOiAiICINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdG9jX2NvbGxhcHNlZDogeWVzDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgc21vb3RoX3Njcm9sbDogeWVzDQogICAgdGhlbWU6IGx1bWVuDQogIHdvcmRfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIGtlZXBfbWQ6IHllcw0KICBwZGZfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogbm8NCiAgICBmaWdfd2lkdGg6IDMNCiAgICBmaWdfaGVpZ2h0OiAzDQplZGl0b3Jfb3B0aW9uczogDQogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUNCi0tLQ0KDQpgYGB7PWh0bWx9DQoNCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoNCi8qIENhc2NhZGluZyBTdHlsZSBTaGVldHMgKENTUykgaXMgYSBzdHlsZXNoZWV0IGxhbmd1YWdlIHVzZWQgdG8gZGVzY3JpYmUgdGhlIHByZXNlbnRhdGlvbiBvZiBhIGRvY3VtZW50IHdyaXR0ZW4gaW4gSFRNTCBvciBYTUwuIGl0IGlzIGEgc2ltcGxlIG1lY2hhbmlzbSBmb3IgYWRkaW5nIHN0eWxlIChlLmcuLCBmb250cywgY29sb3JzLCBzcGFjaW5nKSB0byBXZWIgZG9jdW1lbnRzLiAqLw0KDQpoMS50aXRsZSB7ICAvKiBUaXRsZSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgdGhlIHJlcG9ydCB0aXRsZSAqLw0KICBmb250LXNpemU6IDIycHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBjb2xvcjogRGFya1JlZDsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KICBmb250LWZhbWlseTogIkdpbGwgU2FucyIsIHNhbnMtc2VyaWY7DQp9DQpoNC5hdXRob3IgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGF1dGhvcnMgICovDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogIGNvbG9yOiBuYXZ5Ow0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciB0aGUgZGF0ZSAgKi8NCiAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBjb2xvcjogRGFya0JsdWU7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQpoMSB7IC8qIEhlYWRlciAxIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMSBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMjJweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQpoMiB7IC8qIEhlYWRlciAyIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMiBzZWN0aW9uIHRpdGxlICovDQogICAgZm9udC1zaXplOiAyMHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQoNCmgzIHsgLyogSGVhZGVyIDMgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDMgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDQgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgNCBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpib2R5IHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQ0KDQouaGlnaGxpZ2h0bWUgeyBiYWNrZ3JvdW5kLWNvbG9yOnllbGxvdzsgfQ0KDQpwIHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQ0KDQo8L3N0eWxlPg0KYGBgDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KIyBjb2RlIGNodW5rIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBSIGNvZGUsIHdhcm5pbmdzLCBhbmQgb3V0cHV0IA0KIyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuDQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikNCiAgIGxpYnJhcnkoa25pdHIpDQp9DQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQp9DQppZiAoIXJlcXVpcmUoInBhbG1lcnBlbmd1aW5zIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBhbG1lcnBlbmd1aW5zIikNCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpDQp9DQppZiAoIXJlcXVpcmUoInBsb3RseSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KbGlicmFyeShwbG90bHkpDQp9DQppZiAoIXJlcXVpcmUoIkdHYWxseSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJHR2FsbHkiKQ0KbGlicmFyeShHR2FsbHkpDQp9DQppZiAoIXJlcXVpcmUoIm5hbmlhciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJuYW5pYXIiKQ0KbGlicmFyeShuYW5pYXIpDQp9DQppZiAoIXJlcXVpcmUoInBvb2wiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicG9vbCIpDQpsaWJyYXJ5KHBvb2wpDQp9DQppZiAoIXJlcXVpcmUoIkRCSSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJEQkkiKQ0KbGlicmFyeShEQkkpDQp9DQppZiAoIXJlcXVpcmUoIlJNeVNRTCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJSTXlTUUwiKQ0KbGlicmFyeShSTXlTUUwpDQp9DQppZiAoIXJlcXVpcmUoInJhbmRvbUZvcmVzdCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJyYW5kb21Gb3Jlc3QiKQ0KbGlicmFyeShyYW5kb21Gb3Jlc3QpDQp9DQppZiAoIXJlcXVpcmUoImdnaXJhcGgiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dpcmFwaCIpDQpsaWJyYXJ5KGdnaXJhcGgpDQp9DQppZiAoIXJlcXVpcmUoImhpZ2hjaGFydGVyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImhpZ2hjaGFydGVyIikNCmxpYnJhcnkoaGlnaGNoYXJ0ZXIpDQp9DQppZiAoIXJlcXVpcmUoImJyb29tIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImJyb29tIikNCmxpYnJhcnkoYnJvb20pDQp9DQojIyANCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAjIGluY2x1ZGUgY29kZSBjaHVuayBpbiB0aGUgb3V0cHV0IGZpbGUNCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsIyBzb21ldGltZXMsIHlvdSBjb2RlIG1heSBwcm9kdWNlIHdhcm5pbmcgbWVzc2FnZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgeW91IGNhbiBjaG9vc2UgdG8gaW5jbHVkZSB0aGUgd2FybmluZyBtZXNzYWdlcyBpbg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRoZSBvdXRwdXQgZmlsZS4gDQogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IFRSVUUsICMgeW91IGNhbiBhbHNvIGRlY2lkZSB3aGV0aGVyIHRvIGluY2x1ZGUgdGhlIG91dHB1dA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGluIHRoZSBvdXRwdXQgZmlsZS4NCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BDQogICAgICAgICAgICAgICAgICAgICAgKSAgDQpgYGANCg0KXA0KDQojIEludHJvZHVjdGlvbg0KDQpUZWNobmljYWwgY29tbXVuaWNhdGlvbiwgaW5jbHVkaW5nIHJlcG9ydCB3cml0aW5nIGFuZCB2aXN1YWwgcmVwcmVzZW50YXRpb24sIGlzIGEgY3JpdGljYWwgYXNwZWN0IG9mIGRhdGEgc2NpZW5jZSBhbmQgbWFjaGluZSBsZWFybmluZyAoTUwpLiBCeSBjb21iaW5pbmcgY2xlYXIgcmVwb3J0IHdyaXRpbmcgYW5kIGNvbXBlbGxpbmcgdmlzdWFsIHJlcHJlc2VudGF0aW9uLCBkYXRhIHByb2Zlc3Npb25hbHMgY2FuIHVubG9jayB0aGUgZnVsbCBwb3RlbnRpYWwgb2YgdGhlaXIgYW5hbHlzZXMsIGZvc3RlcmluZyB1bmRlcnN0YW5kaW5nLCB0cnVzdCwgYW5kIGltcGFjdGZ1bCBkZWNpc2lvbi1tYWtpbmcuIFdoZXRoZXIgZXhwbGFpbmluZyBtb2RlbCByZXN1bHRzLCBwZXJzdWFkaW5nIHN0YWtlaG9sZGVycywgb3IgZG9jdW1lbnRpbmcgcHJvY2Vzc2VzLCBlZmZlY3RpdmUgY29tbXVuaWNhdGlvbiBlbnN1cmVzIHRoZSB3b3JrIGNyZWF0ZXMgdGFuZ2libGUgdmFsdWUuDQoNClRoZXNlIHNraWxscyBicmlkZ2UgdGhlIGdhcCBiZXR3ZWVuIGNvbXBsZXggdGVjaG5pY2FsIHdvcmsgYW5kIGFjdGlvbmFibGUgaW5zaWdodHMsIGVuc3VyaW5nIHRoYXQgZGF0YS1kcml2ZW4gZGVjaXNpb25zIGFyZSBlZmZlY3RpdmVseSBjb21tdW5pY2F0ZWQgdG8gZGl2ZXJzZSBhdWRpZW5jZXMuIA0KDQoNCiMgRWZmZWN0aXZlIFZpc3VhbCBSZXByZXNlbnRhdGlvbg0KDQpWaXN1YWwgcmVwcmVzZW50YXRpb24gaGFzIGJlY29tZSBhIGNvcm5lcnN0b25lIG9mIGVmZmVjdGl2ZSBjb21tdW5pY2F0aW9uLiBGcm9tIHNpbXBsZSBiYXIgY2hhcnRzIHRvIGNvbXBsZXggaW50ZXJhY3RpdmUgZGFzaGJvYXJkcywgdmlzdWFsaXphdGlvbnMgc2VydmUgYXMgcG93ZXJmdWwgdG9vbHMgZm9yIHN1bW1hcml6aW5nLCBhbmFseXppbmcsIGFuZCBjb252ZXlpbmcgaW5mb3JtYXRpb24uIFRoZSBzdWNjZXNzIG9mIGFueSB2aXN1YWwgcmVwcmVzZW50YXRpb24gaGluZ2VzIG9uIGl0cyBncmFwaGljYWwgY29tcG9uZW50cywgd2hpY2ggYXJlIHRoZSBmdW5kYW1lbnRhbCBlbGVtZW50cyB0aGF0IGJyaW5nIGRhdGEgdG8gbGlmZS4gVGhlc2UgY29tcG9uZW50cywgd2hlbiB1c2VkIGVmZmVjdGl2ZWx5LCBlbnN1cmUgY2xhcml0eSwgcHJlY2lzaW9uLCBhbmQgZW5nYWdlbWVudC4NCg0KDQojIyBSZXF1aXJlZCBHcmFwaGljYWwgQ29tcG9uZW50cw0KDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0iR3JhcGhpY2FsIGVsZW1lbnRzIGluIGFuIGVmZmVjdGl2ZSB2aXN1YWwgcmVwcmVzZW50YXRpb24gb24gY29ycmVsYXRpb24gYW5kIG51bWVyaWNhbCB2YXJpYWJsZXMgYW5kIHRoZWlyIGRpc3RyaWJ1dGlvbnMuIn0NCmNvbDAgPSBjKCIjNDY4MkI0IiwgIiNCNDQ2NEIiLCAiI0I0QUY0NiIpDQpjb2wudmVjID0gaWZlbHNlKHBlbmd1aW5zJHNwZWNpZXMgPT0gIkFkZWxpZSIsICIjNDY4MkI0IiwgDQogICAgICAgICAgICAgICAgIGlmZWxzZShwZW5ndWlucyRzcGVjaWVzID09ICJDaGluc3RyYXAiLCAiI0I0NDY0QiIsICIjQjRBRjQ2IikpDQpwYXIobWZyb3c9YygxLDIpLCBvbWE9YygwLDAsMiwwKSkNCmxheW91dCA8LSBsYXlvdXQobWF0cml4KGMoMSwxLDIsMyksIDIsIDIsIGJ5cm93ID0gRikpIA0KbGF5b3V0IDwtIHBsb3QocGVuZ3VpbnMkYmlsbF9sZW5ndGhfbW0sIHBlbmd1aW5zJGJpbGxfZGVwdGhfbW0sIA0KICAgICAgICBjb2w9IGNvbC52ZWMsDQogICAgICAgIHBjaD0xNiwNCiAgICAgICAgeGxhYj0iQmlsbCBEZXB0aCAobW0pIiwNCiAgICAgICAgeWxhYj0iQmlsbCBMZW5ndGggKG1tKSIsDQogICAgICAgIG1haW49IkE6IEJpbGwgTGVuZ3RoIGJ5IEJpbGwgRGVwdGggIiwNCiAgICAgICAgY2V4Lm1haW4gPSAwLjksDQogICAgICAgIGNvbC5tYWluID0gImJsdWUiKQ0KICAgICAgICBsZWdlbmQoImJvdHRvbXJpZ2h0IiwgYygiQWRlbGllIiwgIkNoaW5zdHJhcCIsICJHZW50b28iKSwgcGNoPXJlcCgxNiwzKSwNCiAgICAgICAgICAgICAgICBjb2wgPSBjb2wwLA0KICAgICAgICAgICAgICAgIGNleCA9IDAuNywgYnR5ID0gIm4iKQ0KbGF5b3V0IDwtIGJveHBsb3QocGVuZ3VpbnMkYmlsbF9kZXB0aF9tbSB+IHBlbmd1aW5zJHNwZWNpZXMsDQogICAgICAgIHhsYWI9IlNwZWNpZXMiLA0KICAgICAgICB5bGFiPSJCaWxsIERlcHRoIChtbSkiLA0KICAgICAgICBjb2wgPSBjb2wwLA0KICAgICAgICBtYWluPSJCOiBCaWxsIERlcHRoIGJ5IHNwZWNpZXMiLA0KICAgICAgICBjZXgubWFpbiA9IDAuOSwNCiAgICAgICAgY29sLm1haW4gPSAiYmx1ZSIpDQpsYXlvdXQgPC0gYm94cGxvdChwZW5ndWlucyRiaWxsX2xlbmd0aF9tbSB+IHBlbmd1aW5zJHNwZWNpZXMsDQogICAgICAgIHhsYWI9IlNwZWNpZXMiLA0KICAgICAgICB5bGFiPSJCaWxsIExlbmd0aCAobW0pIiwNCiAgICAgICAgY29sID0gY29sMCwNCiAgICAgICAgbWFpbj0iQzogQmlsbCBMZW5ndGggYnkgc3BlY2llcyIsDQogICAgICAgIGNleC5tYWluID0gMC45LA0KICAgICAgICBjb2wubWFpbiA9ICJibHVlIikNCiMjIyANCmxheW91dCA8LSBtdGV4dCgiRGlzdHJpYnV0aW9uIGFuZCBDb29ybGF0aW9uIFBsb3RzIiwgc2lkZSA9IDMsIGxpbmUgPSAtMC41LCBvdXRlciA9IFRSVUUpDQoNCmBgYA0KDQpXZSBuZXh0IGJyaWVmbHkgZXhwbGFpbiB0aGUga2V5IGdyYXBoaWNhbCBjb21wb25lbnRzIGluIHRoZSBhYm92ZSBpbGx1c3RyYXRpdmUgZmlndXJlLg0KDQoNCioqRGF0YSBQb2ludHM6IFRoZSBIZWFydCBvZiB0aGUgVmlzdWFsaXphdGlvbioqDQoNCkF0IHRoZSBjb3JlIG9mIGFueSB2aXN1YWwgcmVwcmVzZW50YXRpb24gYXJlIHRoZSBkYXRhIHBvaW50cy4gVGhlc2UgaW5kaXZpZHVhbCBtYXJrZXJzIGVuY2Fwc3VsYXRlIHRoZSB2YWx1ZXMgb3Igb2JzZXJ2YXRpb25zIHdpdGhpbiB0aGUgZGF0YXNldCwgZm9ybWluZyB0aGUgYmFzaXMgZm9yIHBhdHRlcm5zLCB0cmVuZHMsIG9yIHJlbGF0aW9uc2hpcHMuIEZvciBpbnN0YW5jZSwgaW4gYSBzY2F0dGVyIHBsb3QsIGVhY2ggcG9pbnQgcmVwcmVzZW50cyBhIHVuaXF1ZSBwYWlyaW5nIG9mIHZhcmlhYmxlcywgd2hpbGUgaW4gYSBsaW5lIGNoYXJ0LCBjb25uZWN0ZWQgcG9pbnRzIHJldmVhbCB0aGUgdHJhamVjdG9yeSBvZiBjaGFuZ2Ugb3ZlciB0aW1lLiBXaXRob3V0IGRhdGEgcG9pbnRzLCBhIHZpc3VhbGl6YXRpb24gd291bGQgbGFjayBzdWJzdGFuY2UsIG1ha2luZyB0aGVtIHRoZSBpbmRpc3BlbnNhYmxlIGZvdW5kYXRpb24gb2YgYW55IGdyYXBoaWNhbCBkaXNwbGF5Lg0KDQoqKkF4ZXM6IFRoZSBGcmFtZXdvcmsgb2YgSW50ZXJwcmV0YXRpb24qKg0KDQpBeGVzIHByb3ZpZGUgdGhlIHN0cnVjdHVyYWwgZnJhbWV3b3JrIHRoYXQgYW5jaG9ycyBkYXRhIHBvaW50cyBpbiBhIG1lYW5pbmdmdWwgY29udGV4dC4gVHlwaWNhbGx5IHJlcHJlc2VudGVkIGFzIGhvcml6b250YWwgYW5kIHZlcnRpY2FsIGxpbmVzLCBheGVzIGRlZmluZSB0aGUgY29vcmRpbmF0ZSBzcGFjZSBhbmQgYWxsb3cgdmlld2VycyB0byBpbnRlcnByZXQgdGhlIHNjYWxlIGFuZCBzY29wZSBvZiB0aGUgZGF0YS4gVGhlIFgtYXhpcyBvZnRlbiByZXByZXNlbnRzIGNhdGVnb3JpZXMgb3IgdGltZSBpbnRlcnZhbHMsIHdoaWxlIHRoZSBZLWF4aXMgZGVub3RlcyBudW1lcmljYWwgdmFsdWVzIG9yIG1ldHJpY3MuIFByb3BlciBzY2FsaW5nLCBsYWJlbGluZywgYW5kIHVuaXQgcmVwcmVzZW50YXRpb24gb24gYXhlcyBhcmUgY3JpdGljYWwgZm9yIGVuc3VyaW5nIHRoYXQgZGF0YSBpcyBpbnRlcnByZXRlZCBhY2N1cmF0ZWx5LiBGb3IgZXhhbXBsZSwgaW4gYSBiYXIgY2hhcnQgZGVwaWN0aW5nIHJldmVudWUgZ3Jvd3RoLCBhIHByb3Blcmx5IHNjYWxlZCBZLWF4aXMgc3RhcnRpbmcgYXQgemVybyBwcmV2ZW50cyBtaXNyZXByZXNlbnRhdGlvbiBvZiB0cmVuZHMuDQoNCioqTGFiZWxzOiBHdWlkaW5nIHRoZSBWaWV3ZXIqKg0KDQpMYWJlbHMgYXJlIGVzc2VudGlhbCB0ZXh0dWFsIGVsZW1lbnRzIHRoYXQgcHJvdmlkZSBjbGFyaXR5IGFuZCBjb250ZXh0IHRvIHZpc3VhbCBjb21wb25lbnRzLiBBeGlzIGxhYmVscyBkZXNjcmliZSB0aGUgdmFyaWFibGVzIGJlaW5nIG1lYXN1cmVkLCB3aGlsZSBkYXRhIGxhYmVscyBwaW5wb2ludCBzcGVjaWZpYyB2YWx1ZXMsIGVuaGFuY2luZyB0aGUgcHJlY2lzaW9uIG9mIHRoZSByZXByZXNlbnRhdGlvbi4gRm9yIGluc3RhbmNlLCBhIHBpZSBjaGFydCB3aXRoIGNsZWFybHkgbGFiZWxlZCBzbGljZXMgZW5zdXJlcyB0aGF0IHZpZXdlcnMgY2FuIHF1aWNrbHkgZ3Jhc3AgdGhlIHByb3BvcnRpb25zIG9mIGRpZmZlcmVudCBjYXRlZ29yaWVzLiBUaG91Z2h0ZnVsbHkgZGVzaWduZWQgbGFiZWxzIG5vdCBvbmx5IGFpZCBpbnRlcnByZXRhdGlvbiBidXQgYWxzbyBlbmhhbmNlIHRoZSBvdmVyYWxsIHVzYWJpbGl0eSBvZiB0aGUgdmlzdWFsaXphdGlvbi4NCg0KKipMZWdlbmRzOiBEZWNvZGluZyB0aGUgUmVwcmVzZW50YXRpb24qKg0KDQpMZWdlbmRzIGFjdCBhcyB0aGUga2V5IHRvIHVuZGVyc3RhbmRpbmcgdGhlIHN5bWJvbHMsIGNvbG9ycywgb3IgcGF0dGVybnMgdXNlZCBpbiBhIHZpc3VhbGl6YXRpb24uIFBhcnRpY3VsYXJseSBpbiBtdWx0aS12YXJpYWJsZSByZXByZXNlbnRhdGlvbnMsIGxlZ2VuZHMgZW5hYmxlIHZpZXdlcnMgdG8gZGlmZmVyZW50aWF0ZSBiZXR3ZWVuIGNhdGVnb3JpZXMgb3IgZGF0YXNldHMuIEZvciBleGFtcGxlLCBhIGxpbmUgY2hhcnQgd2l0aCBtdWx0aXBsZSBjb2xvcmVkIGxpbmVzIHJlcHJlc2VudGluZyBkaWZmZXJlbnQgcmVnaW9ucyByZWxpZXMgb24gYSBsZWdlbmQgdG8gY2xhcmlmeSB0aGUgbWFwcGluZyBiZXR3ZWVuIGNvbG9ycyBhbmQgcmVnaW9ucy4gQSB3ZWxsLWRlc2lnbmVkIGxlZ2VuZCBpcyBjb25jaXNlLCBzdHJhdGVnaWNhbGx5IHBsYWNlZCwgYW5kIGFsaWduZWQgd2l0aCB0aGUgb3ZlcmFsbCBkZXNpZ24gb2YgdGhlIHZpc3VhbGl6YXRpb24uDQoNCioqQ29sb3I6IEVuaGFuY2luZyBDb21tdW5pY2F0aW9uKioNCg0KQ29sb3IgaXMgb25lIG9mIHRoZSBtb3N0IHBvd2VyZnVsIHRvb2xzIGluIGEgdmlzdWFsIHJlcHJlc2VudGF0aW9uLCBjYXBhYmxlIG9mIGNvbnZleWluZyBpbmZvcm1hdGlvbiwgZHJhd2luZyBhdHRlbnRpb24sIGFuZCBldm9raW5nIGVtb3Rpb25zLiBTdHJhdGVnaWMgdXNlIG9mIGNvbG9yIGVuaGFuY2VzIHRoZSBpbnRlcnByZXRhYmlsaXR5IG9mIGRhdGEgYnkgZGlmZmVyZW50aWF0aW5nIGNhdGVnb3JpZXMsIGhpZ2hsaWdodGluZyB0cmVuZHMsIG9yIGVtcGhhc2l6aW5nIGNyaXRpY2FsIHBvaW50cy4gRm9yIGV4YW1wbGUsIGEgaGVhdG1hcCB1c2VzIGNvbG9yIGdyYWRpZW50cyB0byByZXByZXNlbnQgaW50ZW5zaXR5IG9yIG1hZ25pdHVkZSwgd2hpbGUgY29udHJhc3RpbmcgY29sb3JzIGluIGEgYmFyIGNoYXJ0IGRpc3Rpbmd1aXNoIGJldHdlZW4gY2F0ZWdvcmllcy4gQWNjZXNzaWJpbGl0eSBjb25zaWRlcmF0aW9ucywgc3VjaCBhcyB1c2luZyBjb2xvcmJsaW5kLWZyaWVuZGx5IHBhbGV0dGVzLCBhcmUgZXNzZW50aWFsIHRvIGVuc3VyZSBpbmNsdXNpdml0eSBhbmQgYnJvYWQgdXNhYmlsaXR5Lg0KDQoqKlRpdGxlcywgQ2FwdGlvbnMsIGFuZCBBbm5vdGF0aW9uczogUHJvdmlkaW5nIENvbnRleHQqKg0KDQpUaXRsZXMsIGNhcHRpb25zLCBhbmQgYW5ub3RhdGlvbnMgYXJlIHRleHR1YWwgY29tcG9uZW50cyB0aGF0IG9mZmVyIGNvbnRleHQsIHN1bW1hcml6ZSBpbnNpZ2h0cywgb3IgZXhwbGFpbiBzcGVjaWZpYyBhc3BlY3RzIG9mIHRoZSB2aXN1YWxpemF0aW9uLiBUaGUgdGl0bGUgYWN0cyBhcyB0aGUgaGVhZGxpbmUsIHN1Y2NpbmN0bHkgc3RhdGluZyB0aGUgcHVycG9zZSBvciB0YWtlYXdheSBvZiB0aGUgdmlzdWFsLiBDYXB0aW9ucyBwcm92aWRlIHN1cHBsZW1lbnRhcnkgaW5mb3JtYXRpb24sIHN1Y2ggYXMgZGF0YSBzb3VyY2VzIG9yIG1ldGhvZG9sb2d5LCB3aGlsZSBhbm5vdGF0aW9ucyBoaWdobGlnaHQga2V5IHRyZW5kcywgb3V0bGllcnMsIG9yIGJlbmNobWFya3MuIFRvZ2V0aGVyLCB0aGVzZSBjb21wb25lbnRzIGd1aWRlIHZpZXdlcnMgdGhyb3VnaCB0aGUgZGF0YSBzdG9yeSBhbmQgcmVpbmZvcmNlIHRoZSBpbnRlbmRlZCBtZXNzYWdlLg0KDQpcDQoNCkluIHN1bW1hcnksIGVhY2ggZ3JhcGhpY2FsIGNvbXBvbmVudCBjb250cmlidXRlcyB1bmlxdWVseSB0byB0aGUgY2xhcml0eSBhbmQgZnVuY3Rpb25hbGl0eSBvZiBhIHZpc3VhbGl6YXRpb24uIFdoZW4gdGhvdWdodGZ1bGx5IGludGVncmF0ZWQsIHRoZXNlIGVsZW1lbnRzIHdvcmsgdG9nZXRoZXIgdG8gY3JlYXRlIGEgZGVzaWduIHRoYXQgaXMgaW50dWl0aXZlLCB2aXN1YWxseSBhcHBlYWxpbmcsIGFuZCBlZmZlY3RpdmUgaW4gY29tbXVuaWNhdGluZyBpdHMgbWVzc2FnZS4NCg0KQW4gaW50dWl0aXZlIHZpc3VhbGl6YXRpb24gZW5zdXJlcyB0aGUgYXVkaWVuY2UgY2FuIGludGVycHJldCBpbmZvcm1hdGlvbiBlYXNpbHkgYW5kIGFjY3VyYXRlbHksIHdpdGhvdXQgY29uZnVzaW9uIG9yIGFtYmlndWl0eS4gQWVzdGhldGljcywgc3VjaCBhcyBoYXJtb25pb3VzIGNvbG9yIHNjaGVtZXMgYW5kIGNsZWFuIGxheW91dHMsIGZ1cnRoZXIgZW5oYW5jZSBlbmdhZ2VtZW50IGFuZCBjb21wcmVoZW5zaW9uLiBCeSBiYWxhbmNpbmcgY2xhcml0eSwgZnVuY3Rpb25hbGl0eSwgYW5kIGRlc2lnbiwgYSB3ZWxsLWNyYWZ0ZWQgdmlzdWFsaXphdGlvbiB0cmFuc2Zvcm1zIGRhdGEgaW50byBtZWFuaW5nZnVsIGluc2lnaHRzLCBlbXBvd2VyaW5nIHN0b3J5dGVsbGluZy4NCg0KDQojIyBQdXJwb3NlcyBvZiBWaXogUmVwcmVzZW50YXRpb24NCg0KVmlzdWFsIHJlcHJlc2VudGF0aW9uIHBsYXlzIGEgcGl2b3RhbCByb2xlIGluIHRoZSBmaWVsZCBvZiBkYXRhIHNjaWVuY2UsIGVzcGVjaWFsbHkgZHVyaW5nIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMgKEVEQSkgYW5kIGZlYXR1cmUgZW5naW5lZXJpbmcuIFRoZXNlIHR3byBwaGFzZXMgYXJlIGNyaXRpY2FsIGZvciB1bmRlcnN0YW5kaW5nIGRhdGFzZXRzLCBpZGVudGlmeWluZyBwYXR0ZXJucywgYW5kIHByZXBhcmluZyBkYXRhIGZvciBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscy4gVGhlIHVzZSBvZiB2aXN1YWxpemF0aW9ucyBub3Qgb25seSBzaW1wbGlmaWVzIGNvbXBsZXggZGF0YSBidXQgYWxzbyBlbmFibGVzIGFuYWx5c3RzIHRvIHVuY292ZXIgaW5zaWdodHMgdGhhdCBtYXkgYmUgb2JzY3VyZWQgaW4gcmF3IG51bWJlcnMuIEluIHRoaXMgY29udGV4dCwgdW5kZXJzdGFuZGluZyB0aGUgcHVycG9zZXMgYW5kIHR5cGVzIG9mIHZpc3VhbCByZXByZXNlbnRhdGlvbiBiZWNvbWVzIGVzc2VudGlhbCBmb3IgZWZmZWN0aXZlIGFuYWx5c2lzIGFuZCBmZWF0dXJlIGRldmVsb3BtZW50Lg0KDQpUaGUgcHVycG9zZXMgb2YgdmlzdWFsIHJlcHJlc2VudGF0aW9uIGNhbiBiZSBicm9hZGx5IGNhdGVnb3JpemVkIGludG8gdGhlIGZvbGxvd2luZyBrZXkgYXJlYXMuDQoNCg0KKipVbmRlcnN0YW5kaW5nIERhdGEgRGlzdHJpYnV0aW9uKioNCg0KVmlzdWFsaXphdGlvbnMgaGVscCBkYXRhIHNjaWVudGlzdHMgZXhhbWluZSB0aGUgZGlzdHJpYnV0aW9uIG9mIGluZGl2aWR1YWwgdmFyaWFibGVzLiBDaGFydHMgc3VjaCBhcyBoaXN0b2dyYW1zLCBkZW5zaXR5IHBsb3RzLCBhbmQgYm94IHBsb3RzIGFyZSBjb21tb25seSB1c2VkIHRvIGlkZW50aWZ5IHRoZSBjZW50cmFsIHRlbmRlbmN5LCBzcHJlYWQsIHNrZXduZXNzLCBhbmQgcHJlc2VuY2Ugb2Ygb3V0bGllcnMuIEZvciBpbnN0YW5jZSwgYSBoaXN0b2dyYW0gbWF5IHJldmVhbCB0aGF0IGEgdmFyaWFibGUgaXMgcmlnaHQtc2tld2VkLCBzdWdnZXN0aW5nIHRoZSBuZWVkIGZvciB0cmFuc2Zvcm1hdGlvbiB0byBub3JtYWxpemUgdGhlIGRhdGEuDQoNCioqSWRlbnRpZnlpbmcgUGF0dGVybnMgYW5kIFJlbGF0aW9uc2hpcHMqKg0KDQpEdXJpbmcgRURBLCB2aXN1YWxpemF0aW9ucyByZXZlYWwgcGF0dGVybnMgYW5kIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB2YXJpYWJsZXMuIFNjYXR0ZXIgcGxvdHMsIHBhaXIgcGxvdHMsIGFuZCBoZWF0bWFwcyBhbGxvdyBhbmFseXN0cyB0byBleHBsb3JlIGNvcnJlbGF0aW9ucyBhbmQgZGVwZW5kZW5jaWVzLiBGb3IgZXhhbXBsZSwgYSBzY2F0dGVyIHBsb3QgbWlnaHQgc2hvdyBhIHN0cm9uZyBwb3NpdGl2ZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIGFkdmVydGlzaW5nIHNwZW5kIGFuZCBzYWxlcywgZ3VpZGluZyBmZWF0dXJlIHNlbGVjdGlvbiBmb3IgcHJlZGljdGl2ZSBtb2RlbHMuDQoNCioqRGV0ZWN0aW5nIEFub21hbGllcyBhbmQgT3V0bGllcnMqKg0KDQpPdXRsaWVycyBjYW4gc2lnbmlmaWNhbnRseSBpbXBhY3QgbWFjaGluZSBsZWFybmluZyBtb2RlbHMsIG1ha2luZyB0aGVpciBkZXRlY3Rpb24gY3J1Y2lhbC4gVmlzdWFsIHRvb2xzIGxpa2UgYm94IHBsb3RzIGFuZCBzY2F0dGVyIHBsb3RzIGVuYWJsZSBhbmFseXN0cyB0byBwaW5wb2ludCBhbm9tYWxpZXMgZWZmZWN0aXZlbHkuIEZvciBpbnN0YW5jZSwgYSBib3ggcGxvdCBvZiB0cmFuc2FjdGlvbiBhbW91bnRzIGluIGEgZGF0YXNldCBtYXkgaGlnaGxpZ2h0IHVudXN1YWxseSBoaWdoIHZhbHVlcyBpbmRpY2F0aXZlIG9mIHBvdGVudGlhbCBmcmF1ZC4NCg0KKipWYWxpZGF0aW5nIERhdGEgUXVhbGl0eSoqDQoNClZpc3VhbGl6YXRpb25zIGhlbHAgYXNzZXNzIGRhdGEgcXVhbGl0eSBieSBoaWdobGlnaHRpbmcgbWlzc2luZyB2YWx1ZXMsIGR1cGxpY2F0ZXMsIG9yIGluY29uc2lzdGVudCBwYXR0ZXJucy4gSGVhdG1hcHMgb3IgYmFyIHBsb3RzIGNhbiBiZSB1c2VkIHRvIHZpc3VhbGl6ZSBtaXNzaW5nIGRhdGEgZGlzdHJpYnV0aW9ucywgZ3VpZGluZyB0aGUgY2hvaWNlIG9mIGltcHV0YXRpb24gc3RyYXRlZ2llcy4NCg0KKipHdWlkaW5nIEZlYXR1cmUgRW5naW5lZXJpbmcqKg0KDQpGZWF0dXJlIGVuZ2luZWVyaW5nIGludm9sdmVzIGNyZWF0aW5nIG9yIHRyYW5zZm9ybWluZyBmZWF0dXJlcyB0byBpbXByb3ZlIG1vZGVsIHBlcmZvcm1hbmNlLiBWaXN1YWxpemF0aW9ucyBzdWNoIGFzIGNvcnJlbGF0aW9uIG1hdHJpY2VzIGFuZCBkZWNpc2lvbiBib3VuZGFyeSBwbG90cyBjYW4gaW5zcGlyZSBuZXcgZmVhdHVyZSBjb21iaW5hdGlvbnMgb3IgdHJhbnNmb3JtYXRpb25zLiBGb3IgZXhhbXBsZSwgYSBoaWdoIGNvcnJlbGF0aW9uIGJldHdlZW4gdHdvIHZhcmlhYmxlcyBtaWdodCBzdWdnZXN0IGNyZWF0aW5nIGFuIGludGVyYWN0aW9uIHRlcm0sIHdoaWxlIHBhdHRlcm5zIGluIGEgc2NhdHRlciBwbG90IGNvdWxkIGluZGljYXRlIHRoZSBuZWVkIGZvciBmZWF0dXJlIHNjYWxpbmcuDQoNCioqQ29tbXVuaWNhdGluZyBJbnNpZ2h0cyoqDQoNClZpc3VhbGl6YXRpb25zIGFyZSBhbiBlZmZlY3RpdmUgd2F5IHRvIGNvbW11bmljYXRlIGZpbmRpbmdzIGZyb20gRURBIGFuZCBmZWF0dXJlIGVuZ2luZWVyaW5nIHRvIHN0YWtlaG9sZGVycy4gQ2xlYXIsIGNvbmNpc2UgZ3JhcGhzIGhlbHAgYnJpZGdlIHRoZSBnYXAgYmV0d2VlbiB0ZWNobmljYWwgYW5hbHlzaXMgYW5kIGJ1c2luZXNzIGRlY2lzaW9ucy4gRm9yIGluc3RhbmNlLCBhIGJhciBjaGFydCBpbGx1c3RyYXRpbmcgdGhlIGltcG9ydGFuY2Ugb2Ygc3BlY2lmaWMgZmVhdHVyZXMgY2FuIGd1aWRlIGRpc2N1c3Npb25zIG9uIHJlc291cmNlIGFsbG9jYXRpb24gb3Igc3RyYXRlZ2ljIGZvY3VzLg0KDQoNCiMjIFR5cGVzIG9mIFZpc3VhbCBSZXByZXNlbnRhdGlvbg0KDQpWaXN1YWwgcmVwcmVzZW50YXRpb24gaXMgYSBjb3JuZXJzdG9uZSBvZiBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIChFREEpIGFuZCBmZWF0dXJlIGVuZ2luZWVyaW5nLCBwcm92aWRpbmcgZGF0YSBzY2llbnRpc3RzIHdpdGggdG9vbHMgdG8gdW5kZXJzdGFuZCBkYXRhLCBpZGVudGlmeSBwYXR0ZXJucywgYW5kIHByZXBhcmUgZmVhdHVyZXMgZm9yIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWxzLiBEaWZmZXJlbnQgdHlwZXMgb2YgdmlzdWFsaXphdGlvbnMgc2VydmUgc3BlY2lmaWMgcHVycG9zZXMsIGZyb20gdW5kZXJzdGFuZGluZyB2YXJpYWJsZSBkaXN0cmlidXRpb25zIHRvIGRldGVjdGluZyBvdXRsaWVycyBhbmQgcmVsYXRpb25zaGlwcy4gQmVsb3cgYXJlIHRoZSBwcmltYXJ5IHR5cGVzIG9mIHZpc3VhbCByZXByZXNlbnRhdGlvbiB1c2VkIGluIHRoZXNlIHN0YWdlcyBvZiBkYXRhIHNjaWVuY2UuDQoNCg0KKipVbml2YXJpYXRlIFZpc3VhbGl6YXRpb25zKioNCg0KVW5pdmFyaWF0ZSB2aXN1YWxpemF0aW9ucyBmb2N1cyBvbiB0aGUgZGlzdHJpYnV0aW9uIGFuZCBjaGFyYWN0ZXJpc3RpY3Mgb2YgYSBzaW5nbGUgdmFyaWFibGUsIGhlbHBpbmcgdG8gYXNzZXNzIGl0cyBjZW50cmFsIHRlbmRlbmN5LCB2YXJpYWJpbGl0eSwgYW5kIHBvdGVudGlhbCBvdXRsaWVycy4gRGVwZW5kaW5nIG9uIHRoZSB0eXBlcyBmZWF0dXJlIHZhcmlhYmxlcywgdGhlIGZvbGxvd2luZyBjaGFydHMgYXJlIGNvbW1vbmx5IHVzZWQgaW4gcHJhY3RpY2UNCg0KKiAqKkhpc3RvZ3JhbXMqKjogVXNlZCB0byBkaXNwbGF5IHRoZSBmcmVxdWVuY3kgZGlzdHJpYnV0aW9uIG9mIG51bWVyaWNhbCB2YXJpYWJsZXMsIHJldmVhbGluZyBza2V3bmVzcywgbW9kYWxpdHksIG9yIGdhcHMgaW4gdGhlIGRhdGEuDQoqICoqQm94IFBsb3RzKio6IEhpZ2hsaWdodCB0aGUgc3ByZWFkLCBtZWRpYW4sIHF1YXJ0aWxlcywgYW5kIHBvdGVudGlhbCBvdXRsaWVycywgcHJvdmlkaW5nIGEgY29uY2lzZSBzdW1tYXJ5IG9mIHRoZSB2YXJpYWJsZSdzIGRpc3RyaWJ1dGlvbi4NCiogKipEZW5zaXR5IFBsb3RzKio6IFNtb290aGVuZWQgdmVyc2lvbnMgb2YgaGlzdG9ncmFtcyB0aGF0IHNob3cgdGhlIHByb2JhYmlsaXR5IGRlbnNpdHkgZnVuY3Rpb24sIG1ha2luZyB0aGVtIHVzZWZ1bCBmb3IgY29tcGFyaW5nIGRpc3RyaWJ1dGlvbnMuDQoqICoqQmFyIENoYXJ0cyoqOiBJZGVhbCBmb3IgdmlzdWFsaXppbmcgdGhlIGZyZXF1ZW5jeSBvZiBjYXRlZ29yaWNhbCB2YXJpYWJsZXMsIGdpdmluZyBhbiBvdmVydmlldyBvZiB0aGUgZGF0YXNldCBjb21wb3NpdGlvbi4NCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD00LCBmaWcuY2FwPSJDb21wYXJpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0d28gbnVtZXJpY2FsIHZhcmlhYmxlcyJ9DQojIFNpbXVsYXRpbmcgc2FtcGxlIGRhdGENCmRhdGEgPC0gZGF0YS5mcmFtZShDYXRlZ29yeSA9IHJlcChjKCJBIiwgIkIiKSwgZWFjaCA9IDUwKSwNCiAgICAgICAgICAgICAgICAgICBWYWx1ZSA9IGMocm5vcm0oNTAsIG1lYW4gPSA1KSwgcm5vcm0oNTAsIG1lYW4gPSA3KSkpDQoNCmJveHBsb3QoZGF0YSRWYWx1ZSB+IGRhdGEkQ2F0ZWdvcnksDQogICAgICAgIHhsYWI9IkNhdGVnb3J5IiwNCiAgICAgICAgeWxhYj0iVmFsdWUiLA0KICAgICAgICBjb2wgPSBjKCJza3libHVlIiwgInB1cnBsZSIpLA0KICAgICAgICBtYWluPSJDb21wYXJpc29uIGJldHdlZW4gdHdvIERpc3RyaWJ1dGlvbnMiLA0KICAgICAgICBjZXgubWFpbiA9IDEuMSwNCiAgICAgICAgY29sLm1haW4gPSAibmF2eSIpDQoNCmBgYA0KDQoqKkJpdmFyaWF0ZSBWaXN1YWxpemF0aW9ucyoqDQoNCkJpdmFyaWF0ZSB2aXN1YWxpemF0aW9ucyBleHBsb3JlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB0d28gdmFyaWFibGVzLCB1bmNvdmVyaW5nIGRlcGVuZGVuY2llcyBvciBjb3JyZWxhdGlvbnMgdGhhdCBtYXkgZ3VpZGUgZmVhdHVyZSBzZWxlY3Rpb24gb3IgdHJhbnNmb3JtYXRpb24uDQoNCiogKipTY2F0dGVyIFBsb3RzKio6IFVzZWQgdG8gaWRlbnRpZnkgY29ycmVsYXRpb25zLCBjbHVzdGVycywgb3IgdHJlbmRzIGJldHdlZW4gdHdvIG51bWVyaWNhbCB2YXJpYWJsZXMuDQoqICoqTGluZSBDaGFydHMqKjogU3VpdGFibGUgZm9yIHRlbXBvcmFsIGRhdGEsIHNob3dpbmcgdHJlbmRzIG9yIGNoYW5nZXMgb3ZlciB0aW1lLg0KKiAqKkdyb3VwZWQgQmFyIENoYXJ0cyoqOiBVc2VmdWwgZm9yIGNvbXBhcmluZyBjYXRlZ29yaWVzIGFjcm9zcyBtdWx0aXBsZSBncm91cHMgb3Igc3ViZ3JvdXBzLg0KKiAqKkhlYXRtYXBzKio6IERpc3BsYXkgY29ycmVsYXRpb25zIGJldHdlZW4gdmFyaWFibGVzIHVzaW5nIGNvbG9yIGdyYWRpZW50cywgbWFraW5nIHRoZW0gYSBwb3B1bGFyIGNob2ljZSBmb3IgdmlzdWFsaXppbmcgY29ycmVsYXRpb24gbWF0cmljZXMuDQoNCg0KYGBge3IgIGZpZy5jYXA9IkNvcnJlbGF0aW9uIGJldHdlZW4gc2VwYWwgbGVuZ3RoIGFuZCBwZXRhbCBsZW5ndGguIn0NCiMgTG9hZCBpcmlzIGRhdGFzZXQNCmRhdGEoaXJpcykNCm1yZyA8LSBsaXN0KGwgPSA1MCwgciA9IDUwLCBiID0gNTAsIHQgPSA1MCwgcGFkID0gMjApDQojIw0KcGFsIDwtIGMoIiMxYjllNzciLCAiI2Q5NWYwMiIsICIjNzU3MGIzIikNCnBhbCA8LSBzZXROYW1lcyhwYWwsIGMoInZpcmdpbmljYSIsICJzZXRvc2EiLCAidmVyc2ljb2xvciIpKQ0KIw0KZmlnIDwtIHBsb3RfbHkoZGF0YSA9IGlyaXMsIHggPSB+U2VwYWwuTGVuZ3RoLCB5ID0gflBldGFsLkxlbmd0aCwgDQogICAgICAgICAgICAgICBjb2xvciA9IH5TcGVjaWVzLCBjb2xvcnMgPSBwYWwsDQogICAgICAgICAgICAgICAgbWFya2VyID0gbGlzdChzaXplID0gflNlcGFsLkxlbmd0aCoyKSkgJT4lIA0KICAgICAgIGxheW91dCh0aXRsZSA9ICdDb3JyZWxhdGlvbiBiZXR3ZWVuIFBldGFsIExlbmd0aCBhbmQgU2VwYWwgTGVuZ3RoJywgDQogICAgICAgICAgICAgIHBsb3RfYmdjb2xvciA9ICJ3aGl0ZSIsIA0KICAgICAgICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAnU2VwYWwgTGVuZ3RoIChjbSknKSwgDQogICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAnU2VwYWwgV2lkdGggKGNtKScpLCANCiAgICAgICAgICAgICAgbGVnZW5kID0gbGlzdCh4ID0gMC43LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gMC4xLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlPWxpc3QodGV4dD0nPGI+IFNwZWNpZXMgb2YgSXJpcyA8L2I+JykpLA0KICAgICAgICAgICAgICBtYXJnaW4gPSBtcmcpDQpmaWcNCmBgYA0KDQogDQoqKk11bHRpdmFyaWF0ZSBWaXN1YWxpemF0aW9ucyoqDQoNCk11bHRpdmFyaWF0ZSB2aXN1YWxpemF0aW9ucyBwcm92aWRlIGluc2lnaHRzIGludG8gcmVsYXRpb25zaGlwcyBhbW9uZyB0aHJlZSBvciBtb3JlIHZhcmlhYmxlcywgaGVscGluZyB0byBpZGVudGlmeSBjb21wbGV4IGludGVyYWN0aW9ucy4NCg0KKiAqKlBhaXIgUGxvdHMqKjogT2ZmZXIgYSBncmlkIG9mIHNjYXR0ZXIgcGxvdHMgZm9yIGFsbCBwYWlycyBvZiB2YXJpYWJsZXMsIGFsb25nIHdpdGggaGlzdG9ncmFtcyBmb3IgaW5kaXZpZHVhbCB2YXJpYWJsZXMsIGVuYWJsaW5nIGEgcXVpY2sgb3ZlcnZpZXcgb2YgcmVsYXRpb25zaGlwcy4NCiogKipJbnRlcmFjdGl2ZSAzRCBTY2F0dGVyIFBsb3RzKio6IFZpc3VhbGl6ZSByZWxhdGlvbnNoaXBzIGluIHRocmVlIGRpbWVuc2lvbnMsIGFkZGluZyBkZXB0aCB0byB0aGUgYW5hbHlzaXMgb2YgZmVhdHVyZSBpbnRlcmFjdGlvbnMuICoqY2F1dGlvbioqIC0gc3RhdGljIDNEIHNjYXR0ZXIgc2hvdWxkIGJlIGF2b2lkZWQuDQoqICoqUGFyYWxsZWwgQ29vcmRpbmF0ZXMqKjogUmVwcmVzZW50IG11bHRpcGxlIHZhcmlhYmxlcyBmb3IgZWFjaCBkYXRhIHBvaW50IGFzIGNvbm5lY3RlZCBsaW5lIHNlZ21lbnRzLCB1c2VmdWwgZm9yIHZpc3VhbGl6aW5nIHRyZW5kcyBpbiBoaWdoLWRpbWVuc2lvbmFsIGRhdGEuDQoqICoqQnViYmxlIENoYXJ0cyoqOiBBZGQgYSB0aGlyZCBkaW1lbnNpb24gdG8gc2NhdHRlciBwbG90cyB0aHJvdWdoIGJ1YmJsZSBzaXplLCBvZnRlbiB1c2VkIHRvIGluY29ycG9yYXRlIGEgd2VpZ2h0IG9yIG1hZ25pdHVkZSBmYWN0b3IuDQoNCg0KYGBge3IgZmlnLmNhcD0iUGFpcndpc2Ugc2NhdHRlciBwbG90IG9mIG51bWVyaWNhbCB2YXJpYWJsZXMgaW4gdGhlIGlyaXMgZGF0YSJ9DQpuYW1lcyhpcmlzKSA9IGMoIlNlcGFsLkwiLCAiU2VwYWwuVyIsICAiUGV0YWwuTCIsICJQZXRhbC5XIiwgIlNwZWNpZXMiICkNCnAgPC0gZ2dwYWlycyhpcmlzLCBjb2x1bW5zPTE6NCwgDQogICAgICAgICAgICAgYWVzKGNvbG9yID0gZmFjdG9yKFNwZWNpZXMpLCBhbHBoYSA9IDAuNSksIA0KICAgICAgICAgICAgIHVwcGVyID0gbGlzdChjb250aW51b3VzID0gd3JhcCgiY29yIiwgc2l6ZSA9IDMpKSwNCiAgICAgICAgICAgICBtYXJnaW4gPSBsaXN0KGwgPSA1MCwgciA9IDUwLCBiID0gNTAsIHQgPSA1MCwgcGFkID0gMjApKSArDQogICAgICBnZ3RpdGxlKCJQYWlyd2lzZWQgU2NhdHRlciBQbG90IG9mIElyaXMgRGF0YSIpICsgDQogICAgICB0aGVtZSgNCiAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAxKSwgICAgIyBDZW50ZXItYWxpZ24gdGhlIHRpdGxlDQogICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQoYygxLCAyLCAyLCAxKSwgImNtIikgICMgYWRqdXN0IHRoZSBtYXJnaW4gb2YgdGhlIHBsb3QNCiAgKQ0KICAjDQpnZ3Bsb3RseShwKQ0KYGBgDQoNCg0KKipWaXN1YWxpemF0aW9ucyBmb3IgQ2F0ZWdvcmljYWwgRGF0YSoqDQoNCkNhdGVnb3JpY2FsIGRhdGEgb2Z0ZW4gcmVxdWlyZXMgZGlzdGluY3QgdHlwZXMgb2YgdmlzdWFsIHJlcHJlc2VudGF0aW9uIHRvIGhpZ2hsaWdodCBkaXN0cmlidXRpb25zIGFuZCBjb21wYXJpc29ucy4NCg0KKiAqKlN0YWNrZWQgQmFyIENoYXJ0cyoqOiBEaXNwbGF5IHRoZSBjb21wb3NpdGlvbiBvZiBjYXRlZ29yaWVzIHdpdGhpbiBzdWJncm91cHMsIGFpZGluZyBpbiB0aGUgYW5hbHlzaXMgb2YgcHJvcG9ydGlvbnMuDQoqICoqTW9zYWljIFBsb3RzKio6IFByb3ZpZGUgYSBkZXRhaWxlZCB2aWV3IG9mIHRoZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gdHdvIG9yIG1vcmUgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHRocm91Z2ggcHJvcG9ydGlvbmFsIGFyZWFzLg0KKiAqKlZpb2xpbiBQbG90cyoqOiBDb21iaW5lIGJveCBwbG90cyBhbmQgZGVuc2l0eSBwbG90cyB0byBzaG93IHRoZSBkaXN0cmlidXRpb24gb2YgbnVtZXJpY2FsIGRhdGEgYWNyb3NzIGNhdGVnb3JpZXMuDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NCwgZmlnLmNhcD0iQW4gaWxsdXN0cmF0aXZlIGV4YW1wbGUgb2Ygc3RhY2tlZCBiYXIgY2hhcnQgdG8gY29tcGFyZSB0aGUgZGlzdHJpYnV0aW9ucyBvZiBvbmUgY2F0ZWdvcmljYWwgdmFyaWFibGUgd2l0aGluIGNhdGVnb3JpZXMgb2YgdGhlIG90aGVyIHZhcmlhYmxlLiJ9DQojIEV4YW1wbGUgZGF0YXNldA0KZGF0YSA8LSBkYXRhLmZyYW1lKA0KICBDYXRlZ29yeSA9IGMoIkEiLCAiQSIsICJCIiwgIkIiLCAiQyIsICJDIiksDQogIFN1YmNhdGVnb3J5ID0gYygiWCIsICJZIiwgIlgiLCAiWSIsICJYIiwgIlkiKSwNCiAgVmFsdWUgPSBjKDEwLCAyMCwgMTUsIDI1LCAzMCwgMTApDQopDQojDQojIENyZWF0ZSB0aGUgc3RhY2tlZCBiYXIgY2hhcnQNCnBsb3QgPC0gcGxvdF9seSgNCiAgZGF0YSwNCiAgeCA9IH5DYXRlZ29yeSwNCiAgeSA9IH5WYWx1ZSwNCiAgY29sb3IgPSB+U3ViY2F0ZWdvcnksDQogIHR5cGUgPSAiYmFyIg0KICApICU+JQ0KICBsYXlvdXQoDQogICAgYmFybW9kZSA9ICJzdGFjayIsICMgU3RhY2sgdGhlIGJhcnMNCiAgICB0aXRsZSA9ICJTdGFja2VkIEJhciBDaGFydCIsDQogICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIkNhdGVnb3J5IiksDQogICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlZhbHVlIikNCiAgICkNCiMgRGlzcGxheSB0aGUgcGxvdA0KcGxvdA0KYGBgDQoNCg0KKipTcGVjaWFsaXplZCBWaXN1YWxpemF0aW9ucyBmb3IgRURBKioNCg0KQ2VydGFpbiB2aXN1YWxpemF0aW9ucyBjYXRlciBzcGVjaWZpY2FsbHkgdG8gdGhlIG5lZWRzIG9mIEVEQSwgZm9jdXNpbmcgb24gZGF0YSBxdWFsaXR5IGFuZCBzdHJ1Y3R1cmUuDQoNCiogKipNaXNzaW5nIERhdGEgSGVhdG1hcHMqKjogSGlnaGxpZ2h0IHRoZSBkaXN0cmlidXRpb24gYW5kIGZyZXF1ZW5jeSBvZiBtaXNzaW5nIHZhbHVlcyBhY3Jvc3MgdGhlIGRhdGEgc2V0LCBndWlkaW5nIGltcHV0YXRpb24gc3RyYXRlZ2llcy4NCiogKipIaXN0b2dyYW0gRmFjZXRzKio6IERpc3BsYXkgZGlzdHJpYnV0aW9ucyBvZiBhIG51bWVyaWNhbCB2YXJpYWJsZSBhY3Jvc3MgbGV2ZWxzIG9mIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUuDQoqICoqT3V0bGllciBQbG90cyoqOiBIaWdobGlnaHQgYW5vbWFsaWVzIHVzaW5nIHNjYXR0ZXIgcGxvdHMgb3Igc3BlY2lhbGl6ZWQgdmlzdWFsaXphdGlvbnMgbGlrZSBib3ggcGxvdCB3aGlza2Vycy4NCg0KYGBge3IgIGZpZy5jYXA9IkNvbXBhcmluZyB0aGUgZGlzdHJpYnV0aW9ucyBudW1lcmljYWwgdmFpYWJsZSBhY3Jvc3MgdGhlIGNhdGVnb3JpZXMgb2YgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZS4ifQ0KIyBFeGFtcGxlIGRhdGFzZXQNCmRhdGEgPC0gZGF0YS5mcmFtZSgNCiAgVmFsdWUgPSBjKHJub3JtKDEwMCwgbWVhbiA9IDUpLCBybm9ybSgxMDAsIG1lYW4gPSAxMCkpLA0KICBDYXRlZ29yeSA9IHJlcChjKCJHcm91cCAxIiwgIkdyb3VwIDIiKSwgZWFjaCA9IDEwMCkNCikNCg0KIyBDcmVhdGUgdGhlIGZhY2V0ZWQgaGlzdG9ncmFtDQpnZyA9IGdncGxvdChkYXRhLCBhZXMoeCA9IFZhbHVlLCBmaWxsID0gQ2F0ZWdvcnkpKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyhjb2xvciA9IENhdGVnb3J5KSwgYmlucyA9IDIwLCBhbHBoYSA9IDAuNywgc2l6ZSA9IDEpICsNCiAgZmFjZXRfd3JhcCh+IENhdGVnb3J5LCBuY29sID0gMSkgKyAjIEZhY2V0IGJ5IENhdGVnb3J5DQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJHcm91cCAxIiA9ICJibHVlIiwgIkdyb3VwIDIiID0gImdyZWVuIikpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiR3JvdXAgMSIgPSAibGlnaHRibHVlIiwgIkdyb3VwIDIiID0gImxpZ2h0Z3JlZW4iKSkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkhpc3RvZ3JhbSBGYWNldHMgd2l0aCBDb2xvcmVkIEJvdW5kYXJpZXMiLA0KICAgIHggPSAiVmFsdWUiLA0KICAgIHkgPSAiRnJlcXVlbmN5Ig0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksDQogICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiAjIFJlbW92ZSBsZWdlbmQgaWYgaXQncyByZWR1bmRhbnQNCiAgKQ0KZ2dwbG90bHkoZ2cpDQpgYGANCg0KDQoqKlZpc3VhbGl6YXRpb25zIGZvciBGZWF0dXJlIEVuZ2luZWVyaW5nKioNCg0KRmVhdHVyZSBlbmdpbmVlcmluZyByZWxpZXMgb24gdmlzdWFsaXphdGlvbnMgdG8gYXNzZXNzIHRoZSBlZmZlY3RpdmVuZXNzIG9mIHRyYW5zZm9ybWF0aW9ucywgY29tYmluYXRpb25zLCBhbmQgZmVhdHVyZSBpbXBvcnRhbmNlLg0KDQoqICoqQ29ycmVsYXRpb24gTWF0cmljZXMqKjogRGlzcGxheSBwYWlyd2lzZSBjb3JyZWxhdGlvbnMgYmV0d2VlbiBudW1lcmljYWwgdmFyaWFibGVzLCBoZWxwaW5nIGlkZW50aWZ5IHJlZHVuZGFudCBvciBoaWdobHkgY29ycmVsYXRlZCBmZWF0dXJlcy4NCiogKipGZWF0dXJlIEltcG9ydGFuY2UgUGxvdHMqKjogRGVyaXZlZCBmcm9tIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWxzLCB0aGVzZSBwbG90cyByYW5rIGZlYXR1cmVzIGJhc2VkIG9uIHRoZWlyIGltcGFjdCBvbiBtb2RlbCBwZXJmb3JtYW5jZS4NCiogKipEZWNpc2lvbiBCb3VuZGFyeSBWaXN1YWxpemF0aW9ucyoqOiBTaG93IHRoZSByZWdpb25zIGNyZWF0ZWQgYnkgYSBtb2RlbOKAmXMgcHJlZGljdGlvbnMsIHVzZWZ1bCBmb3IgZXZhbHVhdGluZyBmZWF0dXJlIGNvbWJpbmF0aW9ucy4NCiogKipJbnRlcmFjdGlvbiBQbG90cyoqOiBJbGx1c3RyYXRlIHRoZSBjb21iaW5lZCBlZmZlY3Qgb2YgdHdvIG9yIG1vcmUgZmVhdHVyZXMgb24gYSB0YXJnZXQgdmFyaWFibGUsIG9mdGVuIHVzZWQgaW4gcmVncmVzc2lvbiBvciBjbGFzc2lmaWNhdGlvbiBwcm9ibGVtcy4NCg0KYGBge3IgIGZpZy5jYXA9IkRpc3BsYXkgdGhlIGludGVyYWN0aW9uIGVmZmVjdHMgaW4gYW4gQU5PVkEgbW9kZWwifQ0KIyBFeGFtcGxlIGRhdGFzZXQNCmRhdGEgPC0gZGF0YS5mcmFtZSgNCiAgRmFjdG9yMSA9IHJlcChjKCJMb3ciLCAiSGlnaCIpLCBlYWNoID0gNiksDQogIEZhY3RvcjIgPSByZXAoYygiQSIsICJCIiwgIkMiKSwgdGltZXMgPSA0KSwNCiAgUmVzcG9uc2UgPSBjKDUsIDYsIDcsIDEwLCAxMSwgMTIsIDQsIDUsIDYsIDksIDgsIDEwKQ0KKQ0KDQojIENyZWF0ZSB0aGUgaW50ZXJhY3Rpb24gcGxvdA0KaW50ZXJhY3QgPSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSBGYWN0b3IyLCB5ID0gUmVzcG9uc2UsIGdyb3VwID0gRmFjdG9yMSwgY29sb3IgPSBGYWN0b3IxKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEpICsgICMgTGluZXMgcmVwcmVzZW50aW5nIGludGVyYWN0aW9uDQogIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsgIyBQb2ludHMgZm9yIGRhdGENCiAgbGFicygNCiAgICB0aXRsZSA9ICJJbnRlcmFjdGlvbiBQbG90IiwNCiAgICB4ID0gIkZhY3RvciAyIiwNCiAgICB5ID0gIlJlc3BvbnNlIiwNCiAgICBjb2xvciA9ICJGYWN0b3IgMSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiLCBoanVzdCA9IDAuNSksDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksDQogICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCINCiAgKQ0KZ2dwbG90bHkoaW50ZXJhY3QpDQpgYGANCg0KDQoqKkludGVyYWN0aXZlIFZpc3VhbGl6YXRpb25zKioNCg0KSW50ZXJhY3RpdmUgdmlzdWFsaXphdGlvbnMgZW5oYW5jZSBleHBsb3JhdGlvbiBieSBhbGxvd2luZyB1c2VycyB0byBtYW5pcHVsYXRlIHRoZSBkYXRhIGR5bmFtaWNhbGx5Lg0KDQoqICoqRGFzaGJvYXJkcyoqOiBDb21iaW5lIG11bHRpcGxlIHZpc3VhbGl6YXRpb25zIGludG8gYSBzaW5nbGUgaW50ZXJmYWNlLCBlbmFibGluZyBmaWx0ZXJpbmcsIHpvb21pbmcsIGFuZCBzbGljaW5nIG9mIGRhdGEgZm9yIGRlZXBlciBpbnNpZ2h0cy4NCiogKipHZW9zcGF0aWFsIE1hcHMqKjogVmlzdWFsaXplIGdlb2dyYXBoaWMgZGF0YSB3aXRoIGxheWVycyBvZiBpbnRlcmFjdGl2aXR5LCBzdWNoIGFzIHpvb21pbmcgaW50byBzcGVjaWZpYyByZWdpb25zIG9yIGRpc3BsYXlpbmcgZGV0YWlsZWQgdG9vbHRpcHMuDQoqICoqSW50ZXJhY3RpdmUgU2NhdHRlciBQbG90cyoqOiBBbGxvdyB1c2VycyB0byBob3ZlciBvdmVyIHBvaW50cyBmb3IgZGV0YWlsZWQgaW5mb3JtYXRpb24gb3IgZmlsdGVyIGRhdGEgYmFzZWQgb24gdmFyaWFibGUgcmFuZ2VzLg0KDQpcDQoNCkluIHN1bW1hcnksIHRoZSBkaXZlcnNpdHkgb2YgdmlzdWFsIHJlcHJlc2VudGF0aW9uIGluIEVEQSBhbmQgZmVhdHVyZSBlbmdpbmVlcmluZyB1bmRlcnNjb3JlcyBpdHMgaW1wb3J0YW5jZSBpbiB1bmRlcnN0YW5kaW5nIGFuZCBwcmVwYXJpbmcgZGF0YSBmb3IgYW5hbHlzaXMuIEZyb20gdW5pdmFyaWF0ZSBoaXN0b2dyYW1zIHRvIGludGVyYWN0aXZlIGRhc2hib2FyZHMsIGVhY2ggdHlwZSBzZXJ2ZXMgYSB1bmlxdWUgcHVycG9zZSBpbiBleHBsb3JpbmcgcGF0dGVybnMsIGFzc2Vzc2luZyBkYXRhIHF1YWxpdHksIGFuZCBvcHRpbWl6aW5nIGZlYXR1cmVzIGZvciBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscy4gQnkgc2VsZWN0aW5nIHRoZSBhcHByb3ByaWF0ZSB2aXN1YWxpemF0aW9uIGZvciB0aGUgdGFzayBhdCBoYW5kLCBkYXRhIHNjaWVudGlzdHMgY2FuIHVuY292ZXIgaW5zaWdodHMsIGVuaGFuY2UgY29tbXVuaWNhdGlvbiwgYW5kIGRyaXZlIGJldHRlciBkZWNpc2lvbi1tYWtpbmcuDQoNCg0KIyMgTW9kZWwtYmFzZWQgVmlzdWFsaXphdGlvbg0KDQpWaXN1YWxpemF0aW9uIGlzIHZpdGFsIGluIGRhdGEgc2NpZW5jZSBhbmQgbWFjaGluZSBsZWFybmluZyAoTUwpIGJlY2F1c2UgaXQgdHJhbnNmb3JtcyBkYXRhLCByZXN1bHRzLCBhbmQgbW9kZWwgaW5zaWdodHMgaW50byBjb21wcmVoZW5zaWJsZSB2aXN1YWwgZm9ybXMsIGFpZGluZyBleHBsb3JhdGlvbiwgY29tbXVuaWNhdGlvbiwgYW5kIGRlY2lzaW9uLW1ha2luZy4gDQoNCiMjIyBNb2RlbCBQZXJmb3JtYW5jZSBFdmFsdWF0aW9uDQoNClZpc3VhbGl6YXRpb24gaXMgY3JpdGljYWwgZm9yIGV2YWx1YXRpbmcgYW5kIGNvbXBhcmluZyBtb2RlbCBwZXJmb3JtYW5jZS4NCg0KKipFeGFtcGxlKio6IFJPQyBDdXJ2ZSBmb3IgQ2xhc3NpZmljYXRpb24NCg0KYGBge3J9DQpsaWJyYXJ5KHBST0MpDQojIFNpbXVsYXRlZCBjbGFzc2lmaWNhdGlvbiByZXN1bHRzDQpJRCA9IHNhbXBsZSgxOjE1MCwgMTUwLCByZXBsYWNlID0gRkFMU0UpDQp0cnVlX2xhYmVscyA8LSBjKHJlcCgxLCA1MCksIHJlcCgwLDEwMCkpW0lEXQ0KcHJlZGljdGVkX3Byb2JzIDwtIGMocm5vcm0oNTAsIDMsIDcpLCBybm9ybSgxMDAsIDEwLDQpKVtJRF0NCiMgUGxvdCBST0MgY3VydmUNCnJvY19vYmogPC0gcm9jKHRydWVfbGFiZWxzLCBwcmVkaWN0ZWRfcHJvYnMpDQpzZW4gPSByb2Nfb2JqJHNlbnNpdGl2aXRpZXMNCnNwZSA9IHJvY19vYmokc3BlY2lmaWNpdGllcw0KIyMgDQpwYXIocHR5ID0gInMiKQ0KcGxvdCgxLXNwZSwgc2VuLCB0eXBlID0gImwiLCBsd2QgPSAyLCBsdHkgPSAxLCBjb2wgPSAiYmx1ZSIsIA0KICAgICB4bGFiID0gIjEgLSBzcGVjaWZpY2l0eSIsDQogICAgIHlsYWIgPSAic2Vuc2l0aXZpdHkiLA0KICAgICBtYWluID0gIlJPQyBDdXJ2ZSIpDQphYmxpbmUoMCwxLCBsdHkgPSAyLCBsd2QgPSAyLCBjb2wgPSAicmVkIikNCmxlZ2VuZCgiYm90dG9tcmlnaHQiLCBjKCJtb2RlbC1iYXNlZCBQZXJmb3JtYW5jZSIsICJSYW5kb20gR3Vlc3MiKSwgDQogICAgICAgY29sPWMoImJsdWUiLCAicmVkIiksIGx0eT0xOjIsIGx3ZCA9IDI6MSwgYnR5ID0gIm4iLCBjZXggPSAwLjgpDQpgYGANCg0KQW4gaW50ZXJhY3RpdmUgcGxvdCBwcm92aWRlcyBtb3JlIGdyYW51bGFyIGluZm9ybWF0aW9uIGFib3V0IHRoZSB1bmRlcmx5aW5nIHBsb3QuDQoNCmBgYHtyfQ0KbGlicmFyeShwUk9DKQ0KIyBTaW11bGF0ZWQgY2xhc3NpZmljYXRpb24gcmVzdWx0cw0KSUQgPSBzYW1wbGUoMToxNTAsIDE1MCwgcmVwbGFjZSA9IEZBTFNFKQ0KdHJ1ZV9sYWJlbHMgPC0gYyhyZXAoMSwgNTApLCByZXAoMCwxMDApKVtJRF0NCnByZWRpY3RlZF9wcm9icyA8LSBjKHJub3JtKDUwLCAzLCA3KSwgcm5vcm0oMTAwLCAxMCw0KSlbSURdDQojIFBsb3QgUk9DIGN1cnZlDQpyb2Nfb2JqIDwtIHJvYyh0cnVlX2xhYmVscywgcHJlZGljdGVkX3Byb2JzKQ0Kc2VuID0gcm9jX29iaiRzZW5zaXRpdml0aWVzDQpzcGUgPSByb2Nfb2JqJHNwZWNpZmljaXRpZXMNClNlblNwZSA9IGRhdGEuZnJhbWUoc2VuID0gc2VuLCBzcGUgPSBzcGUpDQojIyANCmdncm9jID0gZ2dwbG90KGRhdGEgPSBTZW5TcGUsIG1hcHBpbmcgPSBhZXMoeCA9KDEtc3BlKSwgeSA9IHNlbikpICsNCiAgICAgICAgZ2VvbV9yZWN0KGFlcyh4bWluID0gMCwgDQogICAgICAgICAgICAgICAgICAgICAgeG1heCA9IDEsIA0KICAgICAgICAgICAgICAgICAgICAgIHltaW4gPSAwLCANCiAgICAgICAgICAgICAgICAgICAgICB5bWF4ID0gMSksDQogICAgICAgICAgICAgICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIA0KICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtncmF5IiwgDQogICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDAuNCAgICMgZGVmYXVsdCBpcyAwLjUNCiAgICAgICAgICAgICAgICAgICkgKw0KICAgICAgICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwxKSwgeWxpbSA9IGMoMCwxKSkgKw0KICAgICAgICAjZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIiwgc3BhbiA9IDAuMSwgY29sID0gImRhcmtibHVlIikgKyANCiAgICAgICAgI2dlb21fbGluZShjb2wgPSAiZGFya2JsdWUiKSArDQogICAgICAgIGdlb21fcGF0aChjb2wgPSAiZGFya2JsdWUiKSArDQogICAgICAgICNnZW9tX2FibGluZShjb2w9ImRhcmtyZWQiLCBsaW5ldHlwZT0iZGFzaGVkIikgKw0KICAgICAgICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IDEsIHllbmQgPSAxKSwgDQogICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtyZWQiLCBsaW5ldHlwZSA9ICJkYXNoIikgKw0KDQogICAgICAgIGdndGl0bGUoIkludGVyYWN0aXZlIFJPQyBDdXJ2ZSIpICsNCiAgICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dCgNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICJzZXJpZiIsICAjIEZvbnQgZmFtaWx5DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiLCAgICAgIyBGb250IGZhY2UNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIm5hdnkiLCAgICAjIEZvbnQgY29sb3INCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxNCwgICAgICAgICAjIEZvbnQgc2l6ZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3QgPSAwLjUsICAgICAgICMgSG9yaXpvbnRhbCBhZGp1c3RtZW50DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2anVzdCA9IDEsICAgICAgICAgIyBWZXJ0aWNhbCBhZGp1c3RtZW50DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmdsZSA9IDAgICAgICAgICAgIyBGb250IGFuZ2xlDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLA0KICAgICAgICAgICAgICBhc3BlY3QucmF0aW8gPSAxKQ0KICAgICAgICAgICAgDQpnZ3Bsb3RseShnZ3JvYykNCg0KYGBgDQoNCg0KDQoNCiMjIyBVbmRlcnN0YW5kaW5nIE1vZGVsIFByZWRpY3Rpb25zDQoNClZpc3VhbGl6YXRpb24gZXhwbGFpbnMgaG93IG1vZGVscyBhcnJpdmUgYXQgdGhlaXIgcHJlZGljdGlvbnMsIGVzc2VudGlhbCBmb3IgdHJ1c3QgYW5kIHRyYW5zcGFyZW5jeS4NCg0KRXhhbXBsZTogRmVhdHVyZSBJbXBvcnRhbmNlIGluIFJhbmRvbSBGb3Jlc3QNCg0KYGBge3J9DQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkNCiMgRml0IGEgcmFuZG9tIGZvcmVzdCBtb2RlbA0KcmZfbW9kZWwgPC0gcmFuZG9tRm9yZXN0KFNwZWNpZXMgfiAuLCBkYXRhID0gaXJpcywgaW1wb3J0YW5jZSA9IFRSVUUpDQojIFBsb3QgZmVhdHVyZSBpbXBvcnRhbmNlDQp2YXJJbXBQbG90KHJmX21vZGVsLCBtYWluID0gIkZlYXR1cmUgSW1wb3J0YW5jZSBpbiBSYW5kb20gRm9yZXN0IikNCmBgYA0KDQpgYGB7cn0NCnJmX21vZGVsJGltcG9ydGFuY2UNCmBgYA0KDQoNCg0KDQojIFIgSW50ZXJhY3RpdmUgR3JhcGhpY2FsIEZ1bmN0aW9ucw0KDQpJbnRlcmFjdGl2ZSB2aXN1YWxpemF0aW9ucyBoYXZlIGJlY29tZSBhbiBpbmRpc3BlbnNhYmxlIHRvb2wgaW4gbW9kZXJuIGRhdGEgc2NpZW5jZS4gV2hldGhlciB5b3UncmUgcHJlcGFyaW5nIGEgcmVwb3J0LCBhIHByZXNlbnRhdGlvbiwgb3IgYSBkYXNoYm9hcmQsIHByb3ZpZGluZyBpbnRlcmFjdGl2ZSBmaWd1cmVzIGNhbiBtYWtlIHRoZSBkYXRhIG1vcmUgZW5nYWdpbmcsIGluc2lnaHRmdWwsIGFuZCBhY2Nlc3NpYmxlLiBJbiBSLCBzZXZlcmFsIHBvd2VyZnVsIHBhY2thZ2VzIGFsbG93IHlvdSB0byBjcmVhdGUgZHluYW1pYywgaW50ZXJhY3RpdmUgcGxvdHMuIEluIHRoZSBwcmV2aW91cyBzZWN0aW9uLCBJIHVzZWQgYGdncGFpcnNgLCBgcGxvdF9seWAsIGBnZ3Bsb3RseWAsIGFuZCBgbGVhZmxldGAgdG8gaWxsdXN0cmF0ZSB2YXJpb3VzIFIgcGxvdHMuIEluIHRoaXMgc2VjdGlvbiwgd2Ugd2lsbCBjb21wYXJlIHNvbWUgcG9wdWxhciBwYWNrYWdlcyBmb3IgaW50ZXJhY3RpdmUgZGF0YSB2aXN1YWxpemF0aW9ucyBpbiBSLCBpbmNsdWRpbmcgYGdnaXJhcGhgLCBgcGxvdGx5YCwgYGdncGxvdGx5YCwgYW5kIGBoaWdoY2hhcnRlcmAuIFRoZXNlIGxpYnJhcmllcyBhcmUgb2Z0ZW4gdXNlZCBpbiBjb25qdW5jdGlvbiB3aXRoIHRoZSAqKnVtYnJlbGxhLXBhY2thZ2UqKiBgdGlkeXZlcnNlYCBmb3IgZGF0YSBtYW5pcHVsYXRpb24uDQoNCldlIHdpbGwgdXNlIHRoZXNlIGxpYnJhcmllcyB0byB2aXN1YWxpemUgcmVsYXRpb25zaGlwIGJldHdlZW4gdHdvIG51bWVyaWNhbCB2YXJpYWJsZXMgYW5kIHRoZSBkaXN0cmlidXRpb24gb2YgbnVtZXJpY2FsIHZhcmlhYmxlLiBUaGUgZXhhbXBsZXMgYXJlIGJhc2VkIG9uIHRoZSBwb3B1bGFyIGBQYWxtZXIgQXJjaGlwZWxhZ28gKEFudGFyY3RpY2EpIHBlbmd1aW4gZGF0YXNldGAgd2hpY2ggaXMgc2ltaWxhciB0byB0aGUgd2VsbC1rbm93biBgaXJpc2AuIEl0IGlzIGEgZ3JlYXQgaW50cm9kdWN0b3J5IGRhdGEgc2V0IGZvciBkYXRhIGV4cGxvcmF0aW9uIGFuZCB2aXN1YWxpemF0aW9uLiANCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGg9IjYwJSIsIGZpZy5jYXA9IlRocmVlIFBhbG1lciBBcmNoaXBlbGFnbyAoQW50YXJjdGljYSkgcGVuZ3VpbnMifQ0KaW5jbHVkZV9ncmFwaGljcygiaW1nL3Blbmd1aW5JbWFnZS5qcGciKQ0KYGBgDQoNCkEgY29weSBvZiB0aGUgZGF0YSB3aXRoIG1pc3NpbmcgdmFsdWVzIGRlbGV0ZWQgaXMgYXZhaWxhYmxlIGF0IDxodHRwczovL3Blbmdkc2NpLmdpdGh1Yi5pby9TVEE1NTIvdzAyL3Blbmd1aW4uY3N2Pi4gDQoNCmBgYHtyfQ0KcGVuZ3VpbnMgPSByZWFkLmNzdigiaHR0cHM6Ly9wZW5nZHNjaS5naXRodWIuaW8vU1RBNTUyL3cwMi9wZW5ndWluLmNzdiIpDQpgYGANCg0KVGhlIHZhcmlhYmxlcyBhcmUgZGVzY3JpYmVkIGJlbG93Og0KDQoqICoqc3BlY2llcyoqIGEgZmFjdG9yIGRlbm90aW5nIHBlbmd1aW4gc3BlY2llcyAoQWTDqWxpZSwgQ2hpbnN0cmFwIGFuZCBHZW50b28pDQoqICoqaXNsYW5kKiogYSBmYWN0b3IgZGVub3RpbmcgaXNsYW5kIGluIFBhbG1lciBBcmNoaXBlbGFnbywgQW50YXJjdGljYSAoQmlzY29lLCBEcmVhbSBvciBUb3JnZXJzZW4pDQoqICoqYmlsbF9sZW5ndGhfbW0qKiBhIG51bWJlciBkZW5vdGluZyBiaWxsIGxlbmd0aCAobWlsbGltZXRlcnMpDQoqICoqYmlsbF9kZXB0aF9tbSoqIGEgbnVtYmVyIGRlbm90aW5nIGJpbGwgZGVwdGggKG1pbGxpbWV0ZXJzKQ0KKiAqKmZsaXBwZXJfbGVuZ3RoX21tKiogYW4gaW50ZWdlciBkZW5vdGluZyBmbGlwcGVyIGxlbmd0aCAobWlsbGltZXRlcnMpDQoqICoqYm9keV9tYXNzX2cqKiBhbiBpbnRlZ2VyIGRlbm90aW5nIGJvZHkgbWFzcyAoZ3JhbXMpDQoqICoqc2V4KiogYSBmYWN0b3IgZGVub3RpbmcgcGVuZ3VpbiBzZXggKGZlbWFsZSwgbWFsZSkNCiogKip5ZWFyKiogYW4gaW50ZWdlciBkZW5vdGluZyB0aGUgc3R1ZHkgeWVhciAoMjAwNywgMjAwOCwgb3IgMjAwOSkNCg0KIyMgVmlzdWFsaXppbmcgUmVsYXRpb25zaGlwIA0KDQpUd28gb2YgdGhlIDggZmVhdHVyZSB2YXJpYWJsZXMgKipiaWxsX2xlbmd0aF9tbSoqIGFuZCAqKmJpbGxfZGVwdGhfbW0qKiB3aWxsIGJlIHVzZWQgdG8gZGVtb25zdHJhdGUgdGhlIGludGVyYWN0aXZlIHBsb3RzIHRvIHZpc3VhbGl6ZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlbSB3aXRoIGRpZmZlcmVudCBSIGdyYXBoaWNhbCBmdW5jdGlvbnMuDQoNCioqZ2dpcmFwaCoqIA0KDQpUaGUgaW50ZXJhY3RpdmUgZ3JhcGhpY2FsIGZ1bmN0aW9uIGBnZ2lyYXBoKClgIGlzIGEgd3JhcHBlciBvZiAqKmdncGxvdCgpKiouIFdlIGZpcnN0IG1ha2UgYSBnZ3Bsb3QgYW5kIHRoZW4gY2FsbCBgZ2dpcmFwaCgpYCB0byBhZGQgaW50ZXJhY3RpdmUgZmVhdHVyZSB0byB0aGUgZ2dwbG90Lg0KDQoNCmBgYHtyfQ0KZmlnX2Z1bGw9IHBlbmd1aW5zICU+JSANCiAgICAgIGdncGxvdChhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCANCiAgICAgICAgICAgICAgICAgeSA9IGJpbGxfZGVwdGhfbW0sIA0KICAgICAgICAgICAgICAgICBjb2xvciA9IHNwZWNpZXMsIA0KICAgICAgICAgICAgICAgICBzaGFwZSA9IHNwZWNpZXMpKSArIA0KICAgICAgZ2VvbV9wb2ludF9pbnRlcmFjdGl2ZSggDQogICAgICAgICAgICAgICAgYWVzKHRvb2x0aXAgPSBwYXN0ZSgiYmlsbF9sZW5ndGhfbW06IiwgYmlsbF9sZW5ndGhfbW0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj4iLCAiYmlsbF9kZXB0aF9tbToiLCBiaWxsX2RlcHRoX21tKSksIA0KICAgICAgICAgICAgICAgICAgICBzaXplID0gMikgKyANCiAgICAgIGdlb21fc21vb3RoX2ludGVyYWN0aXZlKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSAieSB+IHgiKSArDQogICAgICBsYWJzKHggPSAiQmlsbCBsZW5ndGggKG1tKSIsIA0KICAgICAgICAgICB5ID0gIkJpbGwgd2lkdGggKG1tKSIsIA0KICAgICAgICAgICB0aXRsZSA9ICJCaWxsIGxlbmd0aCB2cy4gYmlsbCB3aWR0aCIsIA0KICAgICAgICAgICBzdWJ0aXRsZSA9ICJVc2luZyB0aGUgZ2dpcmFwaCBwYWNrYWdlIiwNCiAgICAgICAgICAgY29sb3IgPSAiU3BlY2llcyIsIHNoYXBlID0gIlNwZWNpZXMiKSArDQogICAgICB0aGVtZV9taW5pbWFsKCkgKyANCiAgICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiwgICMgRm9udCBmYW1pbHkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiLCAgICAgICAgICMgRm9udCBmYWNlDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAibmF2eSIsICAgICAgICAjIEZvbnQgY29sb3INCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTQsICAgICAgICAgICAgICMgRm9udCBzaXplDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3QgPSAwLjUsICAgICAgICAgICAjIEhvcml6b250YWwgYWRqdXN0bWVudA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZqdXN0ID0gMSwgICAgICAgICAgICAgIyBWZXJ0aWNhbCBhZGp1c3RtZW50DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5nbGUgPSAwLCAgICAgICAgICAgICAjIEZvbnQgYW5nbGUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5laGVpZ2h0ID0gMSksICAgICAgICMgTGluZSBzcGFjaW5nDQogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoICAgICAgICAgICAgICAgICAgICAjIFN1YnRpdGxlIGN1c3RvbWl6YXRpb24NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiLCAgICAgICAgICMgRm9udCBmYWNlDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAibmF2eSIsICAgICAgICAjIEZvbnQgY29sb3INCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTIsICAgICAgICAgICAgICMgRm9udCBzaXplDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3QgPSAwLjUsICAgICAgICAgICAjIEhvcml6b250YWwgYWRqdXN0bWVudA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZqdXN0ID0gMSwgICAgICAgICAgICAgIyBWZXJ0aWNhbCBhZGp1c3RtZW50DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5nbGUgPSAwLCAgICAgICAgICAgICAjIEZvbnQgYW5nbGUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5laGVpZ2h0ID0gMSksICAgICAgICMgTGluZSBzcGFjaW5nDQogICAgICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuMjUpLCAgIyBDYXB0aW9uIGN1c3RvbWl6YXRpb24NCiAgICAgICAgcGxvdC50YWcgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJpdGFsaWMiKSwgICAjIFRhZyBjdXN0b21pemF0aW9uDQogICAgICAgIHBsb3QudGl0bGUucG9zaXRpb24gPSAicGxvdCIsICAgICAgICAgICAgICAgIyBUaXRsZSBhbmQgc3VidGl0bGUgcG9zaXRpb24gDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyAgKCJwbG90IiBvciAicGFuZWwiKQ0KICAgICAgICBwbG90LmNhcHRpb24ucG9zaXRpb24gPSAicGFuZWwiLCAgICAgICAgICAgICMgQ2FwdGlvbiBwb3NpdGlvbiAoInBsb3QiIG9yICJwYW5lbCIpDQogICAgICAgIHBsb3QudGFnLnBvc2l0aW9uID0gInRvcCIsICAgICAgICAgICAgICAgICAgIyBUYWcgcG9zaXRpb24NCiAgICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMCwxLDQsMSksICJjbSIpKSAgICAgICAjIE1hcmdpbnMgKHQsIHIsIGIsIGwpDQpnaXJhZmUoZ2dvYmogPSBmaWdfZnVsbCkNCmBgYA0KDQoqKnBsb3RfbHkoKSoqDQoNCkluIFIsIGBwbG90X2x5KClgIGlzIGEgZnVuY3Rpb24gZnJvbSB0aGUgKipQbG90bHkgcGFja2FnZSoqIHVzZWQgdG8gY3JlYXRlIGludGVyYWN0aXZlIHBsb3RzICpkaXJlY3RseSBieSBzcGVjaWZ5aW5nIHRoZSBkYXRhIGFuZCBwbG90IHR5cGUqLiBJdCBpcyBhIGNvcmUgZnVuY3Rpb24gaW4gdGhlICoqcGxvdGx5IHBhY2thZ2UqKiBhbmQgc2VydmVzIGFzIHRoZSBzdGFydGluZyBwb2ludCBmb3IgYnVpbGRpbmcgdmFyaW91cyB0eXBlcyBvZiB2aXN1YWxpemF0aW9ucyBzdWNoIGFzIHNjYXR0ZXIgcGxvdHMsIGxpbmUgY2hhcnRzLCBiYXIgY2hhcnRzLCBhbmQgbW9yZS4gIFRoZSBrZXkgZmVhdHVyZXMgYXJlIA0KDQoqICoqRHluYW1pYyBJbnRlcmFjdGl2aXR5Kio6IGl0IHN1cHBvcnRzIHpvb21pbmcsIHBhbm5pbmcsIGFuZCB0b29sdGlwcyAoaG92ZXIgZWZmZWN0cykgZm9yIGV4cGxvcmluZyBkYXRhIGludGVyYWN0aXZlbHkuDQoNCiogKipDdXN0b21pemFibGUgVmlzdWFscyoqOiBJdCBhbGxvd3MgY3VzdG9taXphdGlvbiBvZiBjb2xvcnMsIG1hcmtlcnMsIGF4aXMgbGFiZWxzLCBsZWdlbmRzLCBhbmQgbW9yZS4NCg0KKiAqKldpZGUgUmFuZ2Ugb2YgUGxvdCBUeXBlcyoqOiBJdCBoYW5kbGVzIHNjYXR0ZXIgcGxvdHMsIGJhciBjaGFydHMsIGhpc3RvZ3JhbXMsIDNEIHBsb3RzLCBoZWF0bWFwcywgYW5kIG1vcmUuDQoNCiogKipMYXllcmVkIFBsb3R0aW5nKio6IEl0IGVuYWJsZXMgYWRkaW5nIG11bHRpcGxlIHRyYWNlcyAoZGF0YSBsYXllcnMpIGZvciBjcmVhdGluZyBjb21wbGV4IHZpc3VhbGl6YXRpb25zLg0KDQoqICoqSW50ZWdyYXRpb24qKjogSXQgd29ya3Mgc2VhbWxlc3NseSB3aXRoIFIgTWFya2Rvd24sIFNoaW55LCBhbmQgZGFzaGJvYXJkcy4NCg0KYGBge3J9DQpwbG90bHkuZmlnID0gcGVuZ3VpbnMgJT4lIA0KICAgIHBsb3RfbHkoeCA9IH5iaWxsX2xlbmd0aF9tbSwgDQogICAgICAgICAgICB5ID0gfmZsaXBwZXJfbGVuZ3RoX21tLCANCiAgICAgICAgICAgIGNvbG9yID0gfnNwZWNpZXMsIA0KICAgICAgICAgICAgc3ltYm9sID0gfnNwZWNpZXMsDQogICAgICAgICAgICB0eXBlID0gInNjYXR0ZXIiLCANCiAgICAgICAgICAgIG1vZGUgPSAibWFya2VycyIsICANCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3Qoc2l6ZSA9IDEwKSkgJT4lIA0KICAgIGxheW91dCgNCiAgICAgICAgICAgIHBsb3RfYmdjb2xvciA9ICd3aGl0ZScsDQogICAgICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiQmlsbCBMZW5ndGggKG1tKSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHplcm9saW5lID0gRkFMU0UsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHRpY2tsZW4gPSA1KSwNCiAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiRmxpcHBlciBMZW5ndGggKG1tKSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICB6ZXJvbGluZSA9IEZBTFNFLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgdGlja2xlbiA9IDUpLA0KICAgICAgICAgICAgdGl0bGUgPSAiQmlsbCBsZW5ndGggdnMuIGJpbGwgd2lkdGgiLA0KICAgICAgICAgICAgbGVnZW5kID0gbGlzdCh4ID0gMC4wMSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAwLjk5LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGU9bGlzdCh0ZXh0PSc8Yj4gU3BlY2llcyBvZiBQZW5ndWlucyA8L2I+JykpLA0KICAgICAgICAgICAgbWFyZ2luID0gbGlzdChsID0gNTAsIHIgPSA1MCwgYiA9IDUwLCB0ID0gNTAsIHBhZCA9IDIwKQ0KICApIA0KcGxvdGx5LmZpZw0KYGBgDQoNCg0KDQoqKmdncGxvdGx5KioNCg0KYGdncGxvdGx5KClgIGlzIGEgcG93ZXJmdWwgdG9vbCBmb3IgYWRkaW5nIGludGVyYWN0aXZpdHkgdG8gZXhpc3RpbmcgKipnZ3Bsb3QyKiogcGxvdHMgaW4gUi4gSXQgYnJpZGdlcyB0aGUgZ2FwIGJldHdlZW4gc3RhdGljIGFuZCBkeW5hbWljIHZpc3VhbGl6YXRpb25zLCBhbGxvd2luZyB1c2VycyB0byByZXRhaW4gdGhlIGZhbWlsaWFyICoqZ2dwbG90MioqIHdvcmtmbG93IHdoaWxlIGJlbmVmaXRpbmcgZnJvbSAqUGxvdGx5J3MqIGludGVyYWN0aXZpdHkuIFRoaXMgbWFrZXMgaXQgYW4gaWRlYWwgY2hvaWNlIGZvciBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzLCBwcmVzZW50YXRpb25zLCBhbmQgZHluYW1pYyByZXBvcnRpbmcuDQoNClRoZSBrZXkgZmVhdHVyZXMgb2YgYGdncGxvdGx5KClgIGFyZQ0KDQoqICoqSW50ZXJhY3Rpdml0eSoqOiBJdCBhZGRzIHpvb20sIHBhbiwgaG92ZXIgdG9vbHRpcHMsIGFuZCBsZWdlbmQgaW50ZXJhY3Rpb24gdG8gc3RhdGljICoqZ2dwbG90MioqIHBsb3RzLg0KDQoqICoqUHJlc2VydmVzIGdncGxvdDIgQWVzdGhldGljcyoqOiBJdCBtYWludGFpbnMgdGhlIHZpc3VhbCBkZXNpZ24gYW5kIGN1c3RvbWl6YXRpb24gb2YgKipnZ3Bsb3QyKiogcGxvdHMgd2hpbGUgbWFraW5nIHRoZW0gaW50ZXJhY3RpdmUuDQoNCiogKipDb21wYXRpYmlsaXR5Kio6IEl0IHdvcmtzIHNlYW1sZXNzbHkgd2l0aCBnZ3Bsb3QyLWJhc2VkIHBsb3RzIGFuZCBpbnRlZ3JhdGVzIHdlbGwgd2l0aCBSIE1hcmtkb3duLCBTaGlueSBhcHBzLCBhbmQgZGFzaGJvYXJkcy4NCg0KKiAqKkN1c3RvbWl6YXRpb24qKjogSXQgYWxsb3dzIGZ1cnRoZXIgZW5oYW5jZW1lbnQgb2YgaW50ZXJhY3Rpdml0eSBieSBtb2RpZnlpbmcgdG9vbHRpcHMsIGxlZ2VuZHMsIGFuZCBsYXlvdXRzLg0KDQpgYGB7cn0NCmZpZ19mdWxsIDwtIHBlbmd1aW5zICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gYmlsbF9sZW5ndGhfbW0sIHkgPSBiaWxsX2RlcHRoX21tLCANCiAgICAgICAgICAgICBjb2xvciA9IHNwZWNpZXMsIHNoYXBlID0gc3BlY2llcykpICsgDQogIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSAieSB+IHgiKSArDQogIGxhYnMoeCA9ICJCaWxsIGxlbmd0aCAobW0pIiwgDQogICAgICAgeSA9ICJCaWxsIHdpZHRoIChtbSkiLCANCiAgICAgICB0aXRsZSA9ICJCaWxsIGxlbmd0aCB2cy4gYmlsbCB3aWR0aCIsIA0KICAgICAgIHN1YnRpdGxlID0gIlVzaW5nIGdncGxvdDIgYW5kIGdncGxvdGx5KCkgZnJvbSBwbG90bHkiLA0KICAgICAgIGNvbG9yID0gIlNwZWNpZXMiLCANCiAgICAgICBzaGFwZSA9ICJTcGVjaWVzIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiLCAgIyBGb250IGZhbWlseQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2UgPSAiYm9sZCIsICAgICAgICAgIyBGb250IGZhY2UNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJuYXZ5IiwgICAgICAgICMgRm9udCBjb2xvcg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxNCwgICAgICAgICAgICAgIyBGb250IHNpemUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDAuNSwgICAgICAgICAgICMgSG9yaXpvbnRhbCBhZGp1c3RtZW50DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmp1c3QgPSAxLCAgICAgICAgICAgICAjIFZlcnRpY2FsIGFkanVzdG1lbnQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmdsZSA9IDAsICAgICAgICAgICAgICMgRm9udCBhbmdsZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmVoZWlnaHQgPSAxKSwgICAgICAgIyBMaW5lIHNwYWNpbmcNCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dCggICAgICAgICAgICAgICAgICAgICMgU3VidGl0bGUgY3VzdG9taXphdGlvbg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2UgPSAiYm9sZCIsICAgICAgICAgIyBGb250IGZhY2UNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJuYXZ5IiwgICAgICAgICMgRm9udCBjb2xvcg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxMiwgICAgICAgICAgICAgIyBGb250IHNpemUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDAuNSwgICAgICAgICAgICMgSG9yaXpvbnRhbCBhZGp1c3RtZW50DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmp1c3QgPSAxLCAgICAgICAgICAgICAjIFZlcnRpY2FsIGFkanVzdG1lbnQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmdsZSA9IDApLCAgICAgICAgICAgICMgRm9udCBhbmdsZQ0KICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjI1KSwgICMgQ2FwdGlvbiBjdXN0b21pemF0aW9uDQogICAgICAgIHBsb3QudGFnID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiaXRhbGljIiksICAgIyBUYWcgY3VzdG9taXphdGlvbg0KICAgICAgICBwbG90LnRpdGxlLnBvc2l0aW9uID0gInBsb3QiLCAgICAgICAgICAgICAgICMgVGl0bGUgYW5kIHN1YnRpdGxlIHBvc2l0aW9uIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgICgicGxvdCIgb3IgInBhbmVsIikNCiAgICAgICAgcGxvdC5jYXB0aW9uLnBvc2l0aW9uID0gInBhbmVsIiwgICAgICAgICAgICAjIENhcHRpb24gcG9zaXRpb24gKCJwbG90IiBvciAicGFuZWwiKQ0KICAgICAgICBwbG90LnRhZy5wb3NpdGlvbiA9ICJ0b3AiLCAgICAgICAgICAgICAgICAgICMgVGFnIHBvc2l0aW9uDQogICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDEsMSwxLDEpLCAiY20iKSkgICAgICAgIyBNYXJnaW5zICh0LCByLCBiLCBsKQ0KDQoNCmdncGxvdGx5KGZpZ19mdWxsKQ0KYGBgDQoNCioqaGlnaGNoYXJ0ZXIoKSoqDQoNCkluIFIsIGhpZ2hjaGFydGVyIGlzIGEgcGFja2FnZSB0aGF0IHByb3ZpZGVzIGFuIGludGVyZmFjZSB0byB0aGUgSGlnaGNoYXJ0cyBKYXZhU2NyaXB0IGxpYnJhcnksIGFsbG93aW5nIHVzZXJzIHRvIGNyZWF0ZSBoaWdobHkgaW50ZXJhY3RpdmUgYW5kIGN1c3RvbWl6YWJsZSB2aXN1YWxpemF0aW9ucy4gSXQgaXMgcGFydGljdWxhcmx5IHdlbGwtc3VpdGVkIGZvciBjcmVhdGluZyBjaGFydHMgd2l0aCBhZHZhbmNlZCBpbnRlcmFjdGl2aXR5LCBwb2xpc2hlZCBhZXN0aGV0aWNzLCBhbmQgYSB3aWRlIHJhbmdlIG9mIGNoYXJ0IHR5cGVzLCBtYWtpbmcgaXQgcG9wdWxhciBmb3IgZGFzaGJvYXJkcywgcHJlc2VudGF0aW9ucywgYW5kIHdlYi1iYXNlZCByZXBvcnRpbmcuDQoNClRoZSBrZXkgZmVhdHVyZXMgb2YgYGhpZ2hjaGFydGVyKClgOg0KDQoqICoqV2lkZSBSYW5nZSBvZiBDaGFydCBUeXBlcyoqOiBJdCBpbmNsdWRlcyBiYXIgY2hhcnRzLCBsaW5lIGNoYXJ0cywgc2NhdHRlciBwbG90cywgaGVhdG1hcHMsIHRyZWVtYXBzLCBhbmQgbW9yZS4gSXQgYWxzbyBzdXBwb3J0cyBhZHZhbmNlZCB2aXN1YWxpemF0aW9ucyBsaWtlIGdhdWdlIGNoYXJ0cywgc3RvY2sgY2hhcnRzLCBhbmQgbWFwcy4NCg0KKiAqKkludGVyYWN0aXZlIEZlYXR1cmVzKio6IEl0IGFsbG93cyBob3ZlciB0b29sdGlwcywgem9vbWluZywgcGFubmluZywgYW5kIGxlZ2VuZHMuIEl0IGFsc28gYWxsb3dzIGR5bmFtaWMgdXBkYXRlcyBhbmQgYW5pbWF0aW9ucy4NCg0KKiAqKkN1c3RvbWl6YWJpbGl0eSoqOiBJdCBvZmZlcnMgZGV0YWlsZWQgY29udHJvbCBvdmVyIGNoYXJ0IGFlc3RoZXRpY3MsIGluY2x1ZGluZyBjb2xvcnMsIHRoZW1lcywgYXhpcyBsYWJlbHMsIGFuZCB0b29sdGlwcy4NCg0KKiAqKkludGVncmF0aW9uKio6IEl0IHdvcmtzIHNlYW1sZXNzbHkgd2l0aCB0aWR5dmVyc2UgZm9yIGRhdGEgbWFuaXB1bGF0aW9uLiBJdCBpcyBjb21wYXRpYmxlIHdpdGggUiBNYXJrZG93biBhbmQgU2hpbnkgZm9yIGNyZWF0aW5nIGR5bmFtaWMgcmVwb3J0cyBhbmQgZGFzaGJvYXJkcy4NCg0KKiAqKlN1cHBvcnQgZm9yIFRoZW1lcyoqOiBJdCBhbGxvd3MgcHJlZGVmaW5lZCB0aGVtZXMgc3VjaCBhcyBgaGNfdGhlbWVfc21wbCgpYCBhbmQgdGhlIGFiaWxpdHkgdG8gY3JlYXRlIGN1c3RvbSB0aGVtZXMuDQoNCg0KDQpgYGB7cn0NCiNwZW5ndWlucyRjb2xvcnMgPSBpZmVsc2UocGVuZ3VpbnMkc3BlY2llcyA9PSJBZGVsaWUiLCIjNDQwMTU0IiwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHBlbmd1aW5zJHNwZWNpZXMgPT0iQ2hpbnN0cmFwIiwgIiMyMTkwOEMiLCAiI0ZERTcyNSIgKSkNCiMjDQpmaWdfaGlnaGNoYXJ0IDwtIHBlbmd1aW5zICU+JSANCiAgaGNoYXJ0KHR5cGUgPSAic2NhdHRlciIsIA0KICAgICAgICAgaGNhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCANCiAgICAgICAgICAgICAgIHkgPSBmbGlwcGVyX2xlbmd0aF9tbSwNCiAgICAgICAgICAgICAgIGdyb3VwID0gc3BlY2llcyksIA0KICAgICAgICAgbWFya2VyID0gbGlzdChyYWRpdXMgPSA1KSwNCiAgICAgICAgIHNob3dJbkxlZ2VuZCA9IEYpICAlPiUgIA0KICBoY194QXhpcyh0aXRsZSA9IGxpc3QodGV4dCA9ICJCaWxsIGxlbmd0aCAobW0pIikpICU+JSAgDQogIGhjX3lBeGlzKHRpdGxlID0gbGlzdCh0ZXh0ID0gIkJpbGwgd2lkdGggKG1tKSIpKSAlPiUgIA0KICBoY190aXRsZSh0ZXh0ID0gIkJpbGwgbGVuZ3RoIHZzLiBiaWxsIHdpZHRoIikgJT4lIA0KICAjaGNfY29sb3JzKGNvbG9ycykgJT4lDQogIGhjX2xlZ2VuZChhbGlnbiA9ICJsZWZ0IiwgDQogICAgICAgICAgICB2ZXJ0aWNhbEFsaWduID0gInRvcCIsDQogICAgICAgICAgICBsYXlvdXQgPSAidmVydGljYWwiLCANCiAgICAgICAgICAgIHggPSAwLCANCiAgICAgICAgICAgIHkgPSAxMDApDQojIw0Kc3BlY2llc191bmlxdWUgPC0gc29ydCh1bmlxdWUocGVuZ3VpbnMkc3BlY2llcykpDQpjb2xvcnMgPC0gYygiQWRlbGllIiA9ICIjMmNhZmZlIiwgIkNoaW5zdHJhcCIgPSAiIzU0NGZjNSIsICJHZW50b28iID0gIiMwMGUyNzIiKQ0KIyMjDQpmaWdfaGlnaGNoYXJ0MCA9IGZpZ19oaWdoY2hhcnQNCmZvcihqIGluIDE6Mykgew0KICBwZW5ndWluc19zdWJzZXQgPC0gcGVuZ3VpbnMgJT4lICANCiAgICBmaWx0ZXIoc3BlY2llcyA9PSBzcGVjaWVzX3VuaXF1ZVtqXSkNCiAgIyMNCiAgcmVncmVzc2lvbiA8LSBhdWdtZW50KGxtKGZsaXBwZXJfbGVuZ3RoX21tIH4gYmlsbF9sZW5ndGhfbW0sIA0KICAgICAgICAgICAgICAgICAgIGRhdGEgPSBwZW5ndWluc19zdWJzZXQpKQ0KICAjIw0KICBmaWdfaGlnaGNoYXJ0MDEgPC0gZmlnX2hpZ2hjaGFydDAgJT4lICANCiAgICAgIGhjX2FkZF9zZXJpZXMocmVncmVzc2lvbiwgImxpbmUiLCBoY2Flcyh4ID0gYmlsbF9sZW5ndGhfbW0sIHkgPSAuZml0dGVkKSwgDQogICAgICAgICAgICAgICAgICAgIGNvbG9yID0gY29sb3JzW2pdKQ0KICAjIw0KICBmaWdfaGlnaGNoYXJ0MCA9IGZpZ19oaWdoY2hhcnQwMSAgIyB1cGRhdGluZyBtb2RlbCBvYmplY3QgdG8gYWRkIGFub3RoZXIgbGluZQ0KfQ0KIyMNCmZpZ19oaWdoY2hhcnQwMQ0KYGBgDQoNCiMjIFZpc3VhbGl6aW5nIERpc3RyaWJ1dGlvbnMNCg0KSW4gdGhpcyBzdWJzZWN0aW9uLCB3ZSBkcmF3IGRlbnNpdHkgY3VydmVzIG9mIEJNSSBhY3Jvc3MgdGhlIHNwZWNpZXMgb2YgcGVuZ3VpbnMgd2l0aCB0aGUgYWJvdmUgZm91ciBncmFwaGljYWwgZnVuY3Rpb25zLg0KDQoNCioqZ2dpcmFwaCoqDQoNCmBgYHtyfQ0KZmlnX2RlbnNpdHkgPC0gcGVuZ3VpbnMgJT4lIA0KICAgICAgICAgZ2dwbG90KGFlcyh4ID0gYm9keV9tYXNzX2csIGNvbG9yID0gc3BlY2llcywgZmlsbCA9IHNwZWNpZXMpKSArDQogICAgICAgICAgICAgICAgZ2VvbV9kZW5zaXR5X2ludGVyYWN0aXZlKA0KICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHRvb2x0aXAgPSBwYXN0ZSgiU3BlY2llczoiLCBzcGVjaWVzKSksDQogICAgICAgICAgICAgICAgICAgICAgICBsaW5ld2lkdGggPSAwLjc1LCANCiAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC41ICkgICsNCiAgICAgICAgICAgICAgICAgICAgICAgIHhsYWIoIkJNSSBJbmRleCAoZykiKSArICANCiAgICAgICAgICAgZ2d0aXRsZSgiRGVuc2l0eSBDdXJ2ZSBvZiBCTUkgYWNyb3NzIFNwZWNpZXMiKSArDQogICAgICAgICAgIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDAsMSw0LDEpLCAiY20iKSwNCiAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCmdpcmFmZShnZ29iaiA9IGZpZ19kZW5zaXR5KQ0KYGBgDQoNCg0KKipwbG90X2x5KCkqKiANCg0KYGBge3J9DQpBZGVsaWUuQk1JIDwtIHBlbmd1aW5zJGJvZHlfbWFzc19nW3Blbmd1aW5zJHNwZWNpZXM9PSJBZGVsaWUiXQ0KQ2hpbnN0cmFwLkJNSSAgPC0gcGVuZ3VpbnMkYm9keV9tYXNzX2dbcGVuZ3VpbnMkc3BlY2llcz09IkNoaW5zdHJhcCJdDQpHZW50b28uQk1JICA8LSBwZW5ndWlucyRib2R5X21hc3NfZ1twZW5ndWlucyRzcGVjaWVzPT0iR2VudG9vIl0NCkFkZWxpZS5maXQ9IGRlbnNpdHkoQWRlbGllLkJNSSkNCkNoaW5zdHJhcC5maXQ9IGRlbnNpdHkoQ2hpbnN0cmFwLkJNSSkNCkdlbnRvby5maXQ9IGRlbnNpdHkoR2VudG9vLkJNSSkNCiMgQ3JlYXRlIG92ZXJsYXkgZGVuc2l0eSBjdXJ2ZXMNCnBsb3RfbHkoKSAlPiUNCmFkZF90cmFjZSh4ID0gQWRlbGllLmZpdCR4LCANCiAgICAgICAgICB5ID0gQWRlbGllLmZpdCR5LCANCiAgICAgICAgICBtb2RlID0gImxpbmVzIiwgDQogICAgICAgICAgZmlsbCA9ICJ0b3plcm95IiwgDQogICAgICAgICAgeWF4aXMgPSAieTIiLCANCiAgICAgICAgICBuYW1lID0gIkFkZWxpZSIsDQogICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAiYmx1ZSIpLA0KICAgICAgICAgIG9wYWNpdHkgPSAwLjYpICU+JSANCiAgYWRkX3RyYWNlKHggPSBDaGluc3RyYXAuZml0JHgsIA0KICAgICAgICAgIHkgPSBDaGluc3RyYXAuZml0JHksIA0KICAgICAgICAgIG1vZGUgPSAibGluZXMiLCANCiAgICAgICAgICBmaWxsID0gInRvemVyb3kiLCANCiAgICAgICAgICB5YXhpcyA9ICJ5MiIsIA0KICAgICAgICAgIG5hbWUgPSAiQ2hpbnN0cmFwIiwNCiAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJyZWQiKSwNCiAgICAgICAgICBvcGFjaXR5ID0gMC42KSAlPiUNCiAgICBhZGRfdHJhY2UoeCA9IEdlbnRvby5maXQkeCwgDQogICAgICAgICAgeSA9IEdlbnRvby5maXQkeSwgDQogICAgICAgICAgbW9kZSA9ICJsaW5lcyIsIA0KICAgICAgICAgIGZpbGwgPSAidG96ZXJveSIsIA0KICAgICAgICAgIHlheGlzID0gInkyIiwgDQogICAgICAgICAgbmFtZSA9ICJHZW50b28iLA0KICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gInB1cnBsZSIpLA0KICAgICAgICAgIG9wYWNpdHkgPSAwLjYpICU+JQ0KICBsYXlvdXQoDQogICAgICAgIHRpdGxlID0gIkRpc3RyaWJ1dGlvbnMgb2YgQk1JIGFjcm9zcyBQZW5ndWluIFNwZWNpZXMgIiwNCiAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIkJNSSBJbmRleCIpLA0KICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiRGVuc2l0eSIpLA0KICAgICAgICBsZWdlbmQgPSBsaXN0KHRpdGxlID0gbGlzdCh0ZXh0ID0gIlNwZWNpZXMiLCBvcmllbnRhdGlvbj0naCcpKSwNCiAgICAgICAgbWFyZ2luID0gbGlzdChsID0gMTAwLCByID0gNTAsIGIgPSA3MCwgdCA9IDEwMCwgcGFkID0gMjApDQogICAgICkNCmBgYA0KDQoNCioqZ2dwbG90bHkoKSoqDQoNCmBgYHtyfQ0KZ2dwbG90bHlfZGVuc2l0eSA8LSBwZW5ndWlucyAlPiUgDQogIGdncGxvdChhZXMoeCA9IGJvZHlfbWFzc19nLCBjb2xvciA9IHNwZWNpZXMsIGZpbGwgPSBzcGVjaWVzKSkgKw0KICBnZW9tX2RlbnNpdHkobGluZXdpZHRoID0gMC43NSwgYWxwaGEgPSAwLjUpICsNCiAgeGxhYigiQk1JIEluZGV4IChnKSIpICsgDQogIGdndGl0bGUoIkRlbnNpdHkgQ3VydmUgb2YgQk1JIGFjcm9zcyBTcGVjaWVzIikgKw0KICAgICAgICAgICB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYygyLDEsMiwxKSwgImNtIiksDQogICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQpnZ3Bsb3RseShnZ3Bsb3RseV9kZW5zaXR5KQ0KYGBgDQoNCioqaGlnaGNoYXJ0ZXIoKSoqDQoNCmBgYHtyfQ0KQWRlbGllIDwtIHBlbmd1aW5zICU+JSBmaWx0ZXIoc3BlY2llcyA9PSAiQWRlbGllIikNCkNoaW5zdHJhcCA8LSBwZW5ndWlucyAlPiUgZmlsdGVyKHNwZWNpZXMgPT0gIkNoaW5zdHJhcCIpDQpHZW50b28gPC0gcGVuZ3VpbnMgJT4lIGZpbHRlcihzcGVjaWVzID09ICJHZW50b28iKQ0KaGMgPC0gaGNoYXJ0KA0KICAgICAgICAgICAgIGRlbnNpdHkoQWRlbGllJGJvZHlfbWFzc19nKSwgDQogICAgICAgICAgICAgdHlwZSA9ICJhcmVhIiwgDQogICAgICAgICAgICAgY29sb3IgPSAic3RlZWxibHVlIiwgDQogICAgICAgICAgICAgbmFtZSA9ICJBZGVsaWUiKSAlPiUNCiAgICAgIGhjX2FkZF9zZXJpZXMoDQogICAgICAgICAgICAgZGVuc2l0eShDaGluc3RyYXAkYm9keV9tYXNzX2cpLCANCiAgICAgICAgICAgICB0eXBlID0gImFyZWEiLA0KICAgICAgICAgICAgIGNvbG9yID0gIiNCNzFDMUMiLCANCiAgICAgICAgICAgICBuYW1lID0gIkNoaW5zdHJhcCIpICU+JQ0KICAgICAgaGNfYWRkX3NlcmllcygNCiAgICAgICAgICAgICBkZW5zaXR5KEdlbnRvbyRib2R5X21hc3NfZyksIA0KICAgICAgICAgICAgIHR5cGUgPSAiYXJlYSIsDQogICAgICAgICAgICAgY29sb3IgPSAicHVycGxlIiwgDQogICAgICAgICAgICAgbmFtZSA9ICJHZW50b28iKSAlPiUNCiAgICAgIGhjX3RpdGxlKHRleHQgPSAiRGVuc2l0eSBDdXJ2ZXMgb2YgQk1JIGFjcm9zcyBTcGVjaWVzIikgJT4lIA0KICAgICAgaGNfbGVnZW5kKGFsaWduID0gImxlZnQiLCANCiAgICAgICAgICAgIHZlcnRpY2FsQWxpZ24gPSAidG9wIiwNCiAgICAgICAgICAgIGxheW91dCA9ICJ2ZXJ0aWNhbCIsIA0KICAgICAgICAgICAgeCA9IDAsIA0KICAgICAgICAgICAgeSA9IDEwMCkNCmhjDQpgYGANCg==